【Java】Java 常用类

Java 的常用类是 Java SE 的重要部分,这里是学习的笔记,课程选取的是 B 站 UP 主三更草堂,简单的描述了一些常用类的具体使用以及一些注意事项。

Object

Object 类的介绍

Object 类是所有的类的父类,所有的 Java 类都继承了 Object

隐式继承

1
2
3
4
5
public class Demo01 {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

显式继承

1
2
3
4
5
public class Demo02 extends Object {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

构造方法

序号构造方法描述
1Object()构造一个新的对象

常用方法

序号方法描述
01hashCode()获取对象的 hash 值
02getClass()获取对象的运行时对象的类
03toString()返回对象的字符串表示形式
04equals()比较两个对象是否相等
1
2
3
4
5
Object s = new Object();
System.out.println(s.hashCode());
System.out.println(s.getClass());
System.out.println(s.toString());
System.out.println(s.equals(s));
1
2
3
4
1078694789
class java.lang.Object
java.lang.Object@404b9385
true

注意

在 Object 的 equals 方法中的源码如下

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

可以看出在方法中比较的是对象的地址,所以无意义,通常会对方法进行重写

String

String 的介绍

在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串

创建字符串

1
2
String s1 = "abc";
String s2 = new String("abc");

常用方法

序号方法描述
01equals()将此字符串与指定的对象比较
02equalsIgnoreCase()用于将字符串与指定的对象比较,不考虑大小写
03indexOf()返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1
04lastIndexOf()返回指定子字符串在此字符串中最右边出现处的索引,如果此字符串中没有这样的字符,则返回 -1
05length()用于返回字符串的长度,空字符串的长度返回 0
06replace()通过用 newChar 字符替换字符串中出现的所有 searchChar 字符,并返回替换后的新字符串
07split()根据匹配给定的正则表达式来拆分字符串
08substring()返回字符串的子字符串
09startsWith()检测字符串是否以指定的前缀开始
10trim()返回字符串的副本,忽略前导空白和尾部空白
11isEmpty()判断字符串是否为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
String s1 = "abcabc";
String s2 = "ABCABC";
String s3 = " abc ";
String s4 = new String();
System.out.println(s1.equals(s2)); //false
System.out.println(s1.equalsIgnoreCase(s2)); //true
System.out.println(s1.indexOf('b')); //1
System.out.println(s1.lastIndexOf('b')); //4
System.out.println(s1.length()); //6
System.out.println(s1.replace('a','A')); //AbcAbc
System.out.println(s1.substring(2)); //cabc
System.out.println(s1.substring(2,3)); //c
System.out.println(s3.trim()); //abc
System.out.println(s4.isEmpty()); //true

包装类

包装类的介绍

Java 为每一个基本数据类型提供相对应的包装类,提供常用的方法

原始数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

Integer

对象创建

直接创建
1
Integer i = 10;
构造方法创建
1
Integer i2 = new Integer(20);
使用静态方法 valueOf () 创建
1
Integer i3 = Integer.valueOf(30);

常用方法

方法返回值功能描述
byteValue()byte以 byte 类型返回该 Integer 的值
compareTo(Integer anotherInteger)int在数字上比较两个 Integer 对象。如果相同返回 0;调用对象的数值小于 anotherInteger 返回负值,反之,返回正值。
equals(Object IntegerObj)boolean比较此对象与指定对象是否相等
intValue()int以 int 型返回此 Integer 对象
shortValue()short以 short 型返回此 Integer 对象
toString()String返回一个表示该 Integer 值的 String 对象
valueOf(String str)Integer返回指定 Sting 值的 Integer 对象
parseInt(String str)int返回包含在由 str 指定的字符串中的数字的等价整数值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Integer i = 1000;
Integer i2 = new Integer(20);
Integer i3 = Integer.valueOf(30);
//以byte类型返回该Integer的值
System.out.println(i.byteValue()); //-24
//在数字上比较两个Integer对象。如果相同返回0;调用对象的数值小于anotherInteger返回负值,反之,返回正值。
System.out.println(i2.compareTo(i)); //-1
//比较此对象与指定对象是否相等
System.out.println(i.equals(i2));// false
//以int型返回此Integer对象
System.out.println(i.intValue()); //1000
//以short型返回此Integer对象
System.out.println(i.shortValue()); //1000
//返回一个表示该Integer值的String对象
System.out.println(Integer.toString(i)); //1000
//返回指定Sting值的Integer对象
System.out.println(Integer.valueOf("20")); //20
//返回包含在由str指定的字符串中的数字的等价整数值
System.out.println(Integer.parseInt("123")); //123

常用常量

序号常量描述
1MAX_VALUE表示 int 型可取的最大值
2MIN_VALUE表示 int 型可取的最小值
3SIZE表示以 2 进制补码形式表示 int 值的位数
1
2
3
4
5
6
//表示int型可取的最大值
System.out.println(Integer.MAX_VALUE); //2147483647
//表示int型可取的最小值
System.out.println(Integer.MIN_VALUE); //-2147483648
//表示以2进制补码形式表示int值的位数
System.out.println(Integer.SIZE); //32

Boolean

对象创建

1
2
3
4
5
Boolean b = true;
Boolean b2 = Boolean.TRUE;
Boolean b3 = new Boolean(true);
Boolean b4 = new Boolean("true");
Boolean b5 = new Boolean("TRUE");

常用方法

方法返回值功能描述
booleanValue()boolean将 Boolean 对象的值以对应的 boolean 值返回
equals(Object obj)boolean判断调用该方法的对象与 obj 是否相等,当且仅当参数不是 null,而且与调用该方法的对象一样都表示同一个 boolean 值的 Boolean 对象时才返回 true
toString()String返回一个表示该 boolean 值的 String 对象
valueOf(String str)boolean返回一个用指定的字符串表示值的 boolean 值
parseBoolean(String str)boolean将字符串参数解析为 boolean 值

常用常量

序号常量描述
1TRUE对应基值 true 的 Boolean 对象
2FALSE对应基值 false 的 Boolean 对象

Byte

对象创建

1
2
3
Byte b = 20;
byte b1 = 45;
Byte b2 = b;

常用方法

方法返回值功能描述
byteValue()byte以一个 byte 值返回 Byte 对象
compareTo(Byte anotherByte)int在数字上比较两个 Byte 对象 (两数相减)
doubleValue()double以一个 double 值返回 Byte 对象
equals(Object obj)boolean比较此对象与指定对象是否相等
intValue()int以 int 值返回此 Byte 的值
toString()String返回一个 Byte 值的 String 对象
valueOf(String str)byte返回指定 Sting 值的 Byte 对象
parseByte(String str)byte返回包含在由 str 指定的字符串中的数字的等价 byte
1
2
3
4
5
6
7
8
9
10
byte b = 45;
Byte b2 = b;
System.out.println(b2.byteValue()); //45
System.out.println(b2.compareTo((byte) 100)); //-55
System.out.println(b2.doubleValue()); //45.0
System.out.println(b2.equals(b)); //true
System.out.println(b2.intValue()); //45
System.out.println(b2.toString()); //45
System.out.println(Byte.valueOf("40")); //40
System.out.println(Byte.parseByte("50")); //50

常用常量

1
2
System.out.println(Byte.MAX_VALUE);	//127
System.out.println(Byte.MIN_VALUE); //-128

Character

创建对象

1
2
Character c = '1';
Character c1 = new Character('2');

常用方法

方法返回值功能描述
charValue()char返回此 Character 对象的值
compareTo(Character anotherCharacter)int在数字上比较两个 Character 对象。
equals(Object obj)boolean比较此对象与指定对象是否相等
toString()String返回一个表示该 char 值的 String 对象
toUpperCase(char ch)char将字符参数转为大写
toLowerCase(char ch)char将字符参数转为小写
isUpperCase(char ch)boolean判定指定字符参数是否为大写
isLowerCase(char ch)boolean判定指定字符参数是否为小写
1
2
3
4
5
6
7
8
9
10
Character c = '1';
Character c1 = new Character('2');
System.out.println(c.charValue()); //1
System.out.println(c.compareTo('1')); //0
System.out.println(c.equals(c1)); //false
System.out.println(c.toString()); //1
System.out.println(Character.toUpperCase('a')); //A
System.out.println(Character.toLowerCase('A')); //a
System.out.println(Character.isUpperCase('a')); //false
System.out.println(Character.isLowerCase('a')); //true

Double

对象创建

1
2
Double d = 10.0;
Double d1 = new Double(10);

常用方法

方法返回值功能描述
byteValue()byte以 byte 类型返回该 Double 对象的值(通过强制转换)
compareTo(Double d)int在数字上比较两个 Double 对象。如果相同返回 0;调用对象的数值小于 d 返回负值,反之,返回正值。
equals(Object obj)boolean比较此对象与指定对象是否相等
intValue()int以 int 型返回此 Double 对象
isNaN()boolean如果此 double 是非数字,则返回 true
toString()String返回此 Double 对象值的 String 对象
valueOf(String str)double返回指定 Sting 值的 Double 对象
doubleValue()double以 double 形式返回此 Double 对象
longValue()long以 long 形式返回此 Double 对象(通过强制转换)
1
2
3
4
5
6
7
8
9
10
Double d = 10.0;
Double d1 = new Double(10);
System.out.println(d.byteValue()); //10
System.out.println(d.compareTo(d1)); //0
System.out.println(d.equals(d1)); //true
System.out.println(d.intValue()); //10
System.out.println(d.isNaN()); //false
System.out.println(Double.valueOf("100")); //100.0
System.out.println(d.doubleValue()); //10.0
System.out.println(d.longValue()); //10

Number

常用方法

方法返回值功能描述
byteValue()byte以 byte 类型返回指定的数值
intValue()int以 int 类型返回指定的数值
floatValue()float以 float 类型返回指定的数值
shortValue()short以 short 类型返回指定的数值
longValue()long以 long 类型返回指定的数值
doubleValue()double以 double 类型返回指定的数值
1
2
3
4
5
6
7
Number n = 1000;
System.out.println(n.byteValue()); //-24
System.out.println(n.intValue()); //1000
System.out.println(n.floatValue()); //1000.0
System.out.println(n.shortValue()); //1000
System.out.println(n.longValue()); //1000
System.out.println(n.doubleValue()); //1000.0

自动装箱

占时跳过,实现 valueOf () 方法,具体看源码

StringBuffer 和 StringBuilder

区别

String 是不可变的字符串,每对 String 进行次变更操作都会创建一个新的对象。

StringBuilder 是可变字符序列,线程不安全,但效率高。

StringBuffer 是可变字符序列,具有线程安全的特性,相对于 StringBuilder 来说效率较低。

对象创建

1
StringBuffer buffer = new StringBuffer();

String 对字符串的操作

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis(); 
String s = new String();
for (int i = 0; i < 100000; i++) {
s = s+"a";
}
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");//程序运行时间:3754ms

String 对字符串的操作确实效率太低了,只是做了十万次的拼接,就耗费了 3.75s 的时间

StringBuilder 对字符串的操作

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis(); 
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 100000000; i++) {
builder.append('a');
}
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");//程序运行时间:469ms

可以看出,StringBuilder 的性能有多优异,做了一亿次的字符串拼接,只花费了 0.469s 的时间,可以这么讲,相对于 String 的性能来说根本不是一个维度的。

StringBuffer 对字符串的操作

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis(); 
StringBuffer s = new StringBuffer();
for (int i = 0; i < 100000000; i++) {
s.append('a');
}
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");//程序运行时间:1680ms

在前面说到 StringBuffer 的线程是安全的,但性能相对于 StringBuilder 来说是有所下降的,在一亿次的字符拼接拼接中,StringBuffer 花费了 1.68s,确实是有很大的差距。

常用方法

方法返回类型功能描述
append()StringBuilder将指定的字符串追加到此字符序列。
insert()StringBuilder将字符串插入此字符序列.
reverse()StringBuilder导致此字符序列被序列的反向替换
setCharAt(int index, char ch)void指定索引处的字符设置为 ch
1
2
3
4
5
6
7
8
9
10
StringBuffer buffer = new StringBuffer("Hello World");
System.out.println(buffer); //Hello World
buffer.append("Hello Java");
System.out.println(buffer); //Hello WorldHello Java
buffer.insert(10,",");
System.out.println(buffer); //Hello World,Hello Java
buffer.setCharAt(10,'!');
System.out.println(buffer); //Hello Worl!dHello Java
buffer.reverse();
System.out.println(buffer); //avaJ olleHd!lroW olleH

集合框架

常用 list 集合

List 接口下的集合特点:

  • 有索引
  • 可以存储重复元素
  • 元素的顺序和实际存储的顺序相同

ArrayList

创建对象

1
2
ArrayList list = new ArrayList<>();
ArrayList<String> stringArrayList = new ArrayList<>();

常用方法

add()

ArrayList 类提供了很多有用的方法,添加元素到 ArrayList 可以使用 add () 方法

1
2
3
4
5
6
7
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
System.out.println(games); //[Counter-Strike: Global Offensive, Dota 2, PUBG: BATTLEGROUNDS, Apex Legends, Grand Theft Auto V]
get()

访问 ArrayList 中的元素可以使用 get () 方法

1
2
3
4
5
6
7
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
System.out.println(games.get(4)); //Grand Theft Auto V
set()

如果要修改 ArrayList 中的元素可以使用 set () 方法

1
2
3
4
5
6
7
8
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
games.set(0,"CS:GO");//修改下标为0的游戏名称为CS:GO
System.out.println(games); //[CS:GO, Dota 2, PUBG: BATTLEGROUNDS, Apex Legends, Grand Theft Auto V]
remove()

如果要删除 ArrayList 中的元素可以使用 remove () 方法

1
2
3
4
5
6
7
8
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
games.remove(2);
System.out.println(games); //[Counter-Strike: Global Offensive, Dota 2, Apex Legends, Grand Theft Auto V]
size()

如果要计算 ArrayList 中的元素数量可以使用 size () 方法

1
2
3
4
5
6
7
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
System.out.println(games.size()); //5

ArrayList 遍历

for
1
2
3
4
5
6
7
8
9
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
for (int i = 0; i < games.size(); i++) {
System.out.println(games.get(i));
}
for-each
1
2
3
4
5
6
7
8
9
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
for (String game : games) {
System.out.println(game);
}
迭代器 iterator ()

hasNext (),如果迭代有更多元素,则返回 true

1
2
3
4
5
6
7
8
9
10
ArrayList<String> games = new ArrayList<String>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
Iterator<String> iterator = games.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}

并发修改异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在遍历过程中遇到PUBG时将其从ArrayList中移除
ArrayList<String> games = new ArrayList<>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
Iterator<String> iterator = games.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if ("PUBG: BATTLEGROUNDS".equals(next)){
games.remove(next);
}
System.out.println(next);
}

以上代码看似可以从 ArrayList 中移除指定的元素,但实际运行时会产生一个并发修改异常的错误,所以需要对相应的代码进行修改

为什么会产生并发修改异常

首先查看一下错误的信息

1
2
3
4
5
6
7
Counter-Strike: Global Offensive
Dota 2
PUBG: BATTLEGROUNDS
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at com.sangeng.commonClass._03.ArrayListDemo04.main(ArrayListDemo04.java:19)

根据错误信息,可以发现错误发生在 ArrayList.java 的第 1013 行,所以去查看一下源代码

1
2
3
4
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

可以看到,在 modCount 和 expectedModCount 不相等时会抛出一个异常

那么,导致这两个不相等的情况,就很可能是在对 ArrayList 进行修改时发生的,查看 remove () 的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}

通过查看源码,发现这里并没有对这两个的值进行修改啊,那么是不是调用的方法对这两个值进行修改了呢?

在查看 fastRemove () 时,发现这个方法果然对数据进行了修改

1
2
3
4
5
6
7
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}

于是乎,我们来查看一下这个 modCount 到底是什么?

在查看源代码后,大致意思是它表示的是列表结构被修改的次数

在查看 Iterator 接口的实现类时发现

1
int expectedModCount = modCount;

这里看到在对 expectedModCount 进行初始化是用的是 modCount 的值,当接下来 modCount 发生改变后再与 expectedModCount 进行比较就是不相等的,这时便会发生异常

解决思路一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 在遍历过程中遇到PUBG时将其从ArrayList中移除
ArrayList<String> games = new ArrayList<>();
ArrayList<String> remove = new ArrayList<>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
for (int i = games.size() - 1; i >= 0; i--) {
if ("PUBG: BATTLEGROUNDS".equals(games.get(i))){
games.remove(i);
}
}
System.out.println(games); //[Counter-Strike: Global Offensive, Dota 2, Apex Legends, Grand Theft Auto V]
解决思路二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在遍历过程中遇到PUBG时将其从ArrayList中移除
ArrayList<String> games = new ArrayList<>();
ArrayList<String> remove = new ArrayList<>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
Iterator<String> iterator = games.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if ("PUBG: BATTLEGROUNDS".equals(next)){
remove.add(next);
}
}
games.removeAll(remove);
System.out.println(games); //[Counter-Strike: Global Offensive, Dota 2, Apex Legends, Grand Theft Auto V]
foreach 产生并发修改的思考
1
2
3
4
5
for(String game: games){
if ("PUBG: BATTLEGROUNDS".equals(game)){
games.remove(game);
}
}

在使用 foreach 时发现这段代码居然也会产生并发修改异常,而且错误信息和之前完全一样,所以大胆的猜测使用 foreach 遍历 ArrayList 时,使用的依旧是 iterator 来实现的,通过 Debug 发现,果然是调用的 iterator,在 Iterator 接口的实现类的 next () 方法执行时,调用了 checkForComodification (),而由于上一次已经对 games 进行了修改,所以在 checkForComodification () 里进行判断时,modCount 不等于 expectedModCount 条件成立。

toArray()

以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组

1
2
3
4
5
6
7
8
9
10
ArrayList<String> games = new ArrayList<>();
games.add("Counter-Strike: Global Offensive");
games.add("Dota 2");
games.add("PUBG: BATTLEGROUNDS");
games.add("Apex Legends");
games.add("Grand Theft Auto V");
Object[] array = games.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}

LinkedList(链表)

创建对象

1
2
LinkedList linkedList = new LinkedList();
LinkedList<String> strings = new LinkedList<>();

常用方法

方法返回类型描述
add(E e)Boolean链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
add(int index, E element)void向指定位置插入元素。
addFirst(E e)void元素添加到头部。
addLast(E e)void元素添加到尾部。
get(int index)E返回指定位置的元素。
getFirst()E返回第一个元素。
getLast()E返回最后一个元素。
set(int index, E element)E设置指定位置的元素。
removeFirst()E删除并返回第一个元素。
removeLast()E删除并返回最后一个元素。
remove(int index)E删除指定位置的元素。
clear()void清空链表。
contains(Object o)boolean判断是否含有某一元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LinkedList linkedList = new LinkedList();
linkedList.add("Counter-Strike: Global Offensive");
linkedList.add(1,"Dota 2");
linkedList.add("Apex Legends");
linkedList.addFirst("Grand Theft Auto V");
System.out.println(linkedList); //[Grand Theft Auto V, Counter-Strike: Global Offensive, Dota 2, Apex Legends]
System.out.println(linkedList.get(0)); //Grand Theft Auto V
System.out.println(linkedList.getFirst()); //Grand Theft Auto V
System.out.println(linkedList.getLast()); //Apex Legends
linkedList.set(1,"CSGO");
System.out.println(linkedList); //[Grand Theft Auto V, CSGO, Dota 2, Apex Legends]
linkedList.removeFirst();
System.out.println(linkedList); //[CSGO, Dota 2, Apex Legends]
linkedList.removeLast();
System.out.println(linkedList); //[CSGO, Dota 2]
linkedList.remove("CSGO");
System.out.println(linkedList); //[Dota 2]
linkedList.clear();
System.out.println(linkedList); //[]
System.out.println(linkedList.contains("CSGO")); //false

遍历的三种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LinkedList linkedList = new LinkedList();
linkedList.add("Counter-Strike: Global Offensive");
linkedList.add(1,"Dota 2");
linkedList.add("Apex Legends");
linkedList.addFirst("Grand Theft Auto V");
System.out.println("--------------方法一------------------------");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
System.out.println("--------------方法二------------------------");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("--------------方法三------------------------");
for (Object o : linkedList) {
System.out.println(o);
}

ArrayList 和 LinkedList 的区别

都是实现了 List 接口,但底层存储数据的数据结构不同,ArrayList 底层是用数组来存储的,而 Linked 用的则是链表,所以各自的特点跟各自的数据结构特点一样。

ArrayList:查找快,增删慢

LinkedList:增删快,查找慢

常用 set 集合

set 集合的特点

  • 不能存储重复数据

  • 没有索引

HashSet

HashSet 的特点

  • 底层数据结构是哈希表
  • 存储元素的顺序和遍历的顺序可能是不一样的
  • 没有索引
  • 集合不能存储重复元素
  • 基于 HashMap 来实现
  • 允许 null 值
  • 线程是不安全的,多线程操作可能结果有问题
  • 实现了 Set 接口

创建对象

1
2
HashSet hashSet = new HashSet();
HashSet<String> strings = new HashSet<>();

常用方法

方法返回类型描述
add(E e)boolean添加元素,返回是否成功,成功为 true,失败为 false。
contains(E e)boolean判断元素是否存在于集合当中
remove(E e)boolean删除集合中的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
HashSet<String> strings = new HashSet<>();
strings.add("Counter-Strike: Global Offensive");
strings.add("Dota 2");
strings.add("PUBG: BATTLEGROUNDS");
strings.add("Lost Ark");
strings.add("Apex Legends");
System.out.println(strings);
strings.remove("PUBG: BATTLEGROUNDS");
System.out.println(strings);
if (!strings.contains("PUBG")){
strings.add("PUBG");
}
System.out.println(strings);

遍历

与 list 集合类似,由于没有索引,索引不能通过索引来查询,但都是有迭代器 iterator 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HashSet<String> strings = new HashSet<>();
strings.add("Counter-Strike: Global Offensive");
strings.add("Dota 2");
strings.add("PUBG: BATTLEGROUNDS");
strings.add("Lost Ark");
strings.add("Apex Legends");
System.out.println("------方法一-------");
for (String s: strings){
System.out.println(s);
}
System.out.println("------方法二-------");
Iterator<String> stringIterator = strings.iterator();
while (stringIterator.hasNext()){
System.out.println(stringIterator.next());
}

虽然看着是两种方法,其实都是通过 iterator 迭代器来实现的。

泛型

泛型类和泛型接口

  • 泛型类和泛型接口都一样

  • 泛型类就是泛型定义在类上,在使用该类时,才会把类型定义下来

泛型的定义

在类名猴子加 <>,在 <> 中的内容相当于泛型的名字

java 中泛型标记符:

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(Java 类)
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • - 表示不确定的 java 类型
1
2
public class Test<T>{
}

使用泛型

1
2
3
4
5
6
7
public class Box<E> {
private E e;

public void add(E e){
this.e = e;
}
}

在类中定义了泛型类,此时类型是没有确定的,只有在使用时才能确定

1
2
3
4
Box<String> stringBox = new Box<>();
stringBox.add("Java");
Box<Integer> integerBox = new Box<>();
integerBox.add(1);

泛型的确定并不是只有在使用时才确定,还有定义子类时可以确定

1
2
public class DoubleBox extends Box<Double>{
}

Double 类继承了 Box 类,并且指定了类型为 Double

1
2
DoubleBox doubleBox = new DoubleBox();
doubleBox.add(10.0);

泛型接口

首先定义一个泛型接口

1
2
3
public interface Test<T> {
T test(T t);
}

第二步是在接口实现类确定具体的类型

1
2
3
4
5
6
public class TestImpl implements Test<String>{
@Override
public String test(String s) {
return s;
}
}
1
2
Test<String> stringTest = new TestImpl();
String java = stringTest.test("Java");

泛型方法

每次调用方法的时候确定类型

泛型方法的定义

1
2
3
4
5
6
public static <T> T test(T t){
return t;
}
public static <T> void test2(T t){
System.out.println(t);
}

泛型方法的使用

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
System.out.println(test(new String("Java")));
test2(new String("Java"));
}
public static <T> T test(T t){
return t;
}
public static <T> void test2(T t){
System.out.println(t);
}

泛型上限与泛型下限

在特特定的情况下,需要对泛型做一定的限制,所以有了泛型的上限和泛型下限

格式

1
<? extends 具体的类型>

举例

首先创建一个类

1
2
3
4
public class Person {
private String name;
private int age;
}

再创建一个类继承 Person 类

1
2
3
public class Student extends Person{

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ArrayList<Person> people = new ArrayList<>();
ArrayList<Student> students = new ArrayList<>();
people.add(new Person());
students.add(new Student());
test(people);
test(students);
test2(people);
test2(students);

}
public static void test(List<? extends Person> list){
System.out.println(list.toString());
}
public static void test2(List<? super Student> list){
System.out.println(list.toString());
}

在测试方法 test 中,对 List 的泛型做了限制,test 的参数类型 List 的元素只能是 Person 或者继承 Person 的对象,所以在 list.add (1) 出会发生错误,因为不符合 test () 的泛型,而 people 和 students 都符合,所以这里的代码不会出现异常,这就是泛型上限。

在 test2 () 中,对泛型做了限制,List 的元素只能是 Student 或者 Student 的父类,people 和 students 分别是 Person 对象和 Student 对象,所以符合 test2 () 的条件,这就是泛型下限。

Map

Map 集合的概述

Map 接口是双列集合的顶层接口

Map 接口的定义如下

1
public interface Map<K, V>

Map 的特征有如下

  • 存储数据必须包含 key 和 value
  • key 和 value 在 Map 集合中一一对应
  • key 在 Map 中不会重复

HashMap

HashMap 的特征如下

  • 底层数据结构是哈希表
  • 存储元素的顺序和遍历取出来的顺序不一定一样
  • key 不可重复

HashMap 的创建

1
Map<String,String> stringMap = new HashMap<>();

HashMap 的常用方法

方法返回类型描述
put(key,value)value添加键值对 (key-value),如果 key 不存在则添加,如果 key 存在,则更新,添加时返回 null,更新时返回修改的值
get(key)value通过 key 获取 value
remove(key)value删除 Map 中的元素,返回被删除的内容
containsKey(key)boolean判断 key 是否存在
size()int返回 Map 中的键和值的对数
clear()void清空 Map 中的元素
1
2
3
4
5
6
7
8
9
10
11
Map<String,String> stringMap = new HashMap<>();
System.out.println(stringMap.put("a", "A")); //null
System.out.println(stringMap.put("a", "Apple")); //A
System.out.println(stringMap.get("a")); //Apple
System.out.println(stringMap.remove("a")); //Apple
System.out.println(stringMap.containsKey("a")); //false
stringMap.put("b","B");
System.out.println(stringMap.size()); //1
System.out.println(stringMap.toString()); //{b=B}
stringMap.clear();
System.out.println(stringMap.toString()); //{}

HashMap 的遍历

第一种方法是通过 entrySet 方法获取一个 Set 集合,集合中存放这 Entry 对象,通过对象可以获取到里面的键值对。

1
2
3
4
5
6
7
8
9
10
HashMap<Integer,String> gameMap = new HashMap<>();
gameMap.put(1,"CSGO");
gameMap.put(2,"Dota 2");
gameMap.put(3,"PUBG");
gameMap.put(4,"Apex");
gameMap.put(5,"Lost Ark");
Set<Map.Entry<Integer, String>> entries = gameMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+":"+entry.getValue());
}

第二中方法是 keySet 方法,返回一个 Set 集合,返回的是 Map 中 key 的值。

1
2
3
4
5
6
7
8
9
10
HashMap<Integer,String> gameMap = new HashMap<>();
gameMap.put(1,"CSGO");
gameMap.put(2,"Dota 2");
gameMap.put(3,"PUBG");
gameMap.put(4,"Apex");
gameMap.put(5,"Lost Ark");
Set<Integer> integers = gameMap.keySet();
for (Integer key : integers) {
System.out.println(key+":"+gameMap.get(key));
}

File

Java 文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。

File 对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个 File 对象。

通过给定的父抽象路径名和子路径名字符串创建一个新的 File 实例。

创建对象

1
2
3
4
File file = new File("C:\\Users\\haiba\\Desktop\\Java\\IO\\file.txt");
File file1 = new File("C:\\Users\\haiba\\Desktop\\Java\\IO", "file.txt");
File file2 = new File("C:\\Users\\haiba\\Desktop\\Java\\IO");
File file3 = new File(file2, "file.txt");

通过全路径和父子路径来创建 File 对象

常用方法

方法返回类型描述
createNewFile()boolean创建文件,成功返回 TRUE,创建失败返回 false
mkdir()boolean创建文件夹,只能创建一级目录
mkdirs()boolean创建多级文件夹
exists()boolean判断是否存在
isFile()boolean判断是否是文件
isDirectory()boolean判断是否是文件夹
delete()boolean删除文件或者文件夹,删除文件夹是文件夹必须为空
length()long返回文件的大小,对于文件夹无意义
getName()String获取文件名
getParentFile()File获取父级目录对象
getAbsoluteFile()File获取绝对路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File file = new File("C:\\Users\\haiba\\Desktop\\Java\\IO\\test.txt");
boolean newFile = file.createNewFile(); //创建成功返回TRUE,创建失败返回false
File dir = new File("C:\\Users\\haiba\\Desktop\\Java\\IO\\test");
boolean mkdir = dir.mkdir(); //创建文件夹,只能创建直系子目录或者文件
File dirs = new File("C:\\Users\\haiba\\Desktop\\Java\\IO\\a\\b");
boolean mkdirs = dirs.mkdirs(); //创建多级文件夹,返回是否创建成功
boolean exists = dirs.exists(); //判断文件夹或者文件是否存在
boolean isFile = file.isFile(); //判断是否是一个文件
boolean isDirectory = dir.isDirectory(); //判断是否是文件夹
boolean delete = dir.delete(); //删除一个空文件夹,这个文件夹必须是空文件夹
long length = file.length(); //获取文件的大小,对文件夹无意义
String name = file.getName(); //获取文件的文件名
File parentFile = file.getParentFile(); //获取父级目录的对象
File absoluteFile = file.getAbsoluteFile(); //获取文件的绝对路径

重要方法

1
2
3
4
5
File file = new File("C:\\Users\\haiba\\Desktop\\Java\\IO");
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);
}

通过 listFiles () 方法获取当前目录下的所有文件和文件夹对象,但对于当前对象是文件时无意义,返回 null

练习

获取扩展名

1
2
3
4
5
6
7
private static String getExtensionName(File file){
String fileName = file.getName();
if (fileName.lastIndexOf('.') != -1 && fileName.lastIndexOf('.')!=0){
return fileName.substring(fileName.lastIndexOf(".")+1);
}
return "";
}

获取当前目录的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
private static long getSize(File file){
if (file.isFile()){
return file.length();
}
File[] children = file.listFiles();
long size = 0;
if (children!=null){
for (File child : children) {
size += getSize(child);
}
}
return size;
}

递归计算文件夹下所有文件的大小

递归

什么是递归

序调用自身的编程技巧称为递归(recursion),它做为一种算法在程序设计语言中广泛应用。

递归练习 —— 阶乘

阶乘是基斯顿・卡曼于 1808 年发明的运算符号,是数学术语。

一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且 0 的阶乘为 1。自然数 n 的阶乘写作 n!。1808 年,基斯顿・卡曼引进这个表示法。

亦即 n!=1×2×3×…×(n-1)×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n。

既然已经知道了条件,那么用 Java 代码要怎么去表现呢?

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
System.out.println(getFactorial(5));
}
public static long getFactorial(int f){
if (f==1){
return 1;
}
return f * getFactorial(f-1);
}

递归练习 —— 斐波那契数列

什么是斐波那契数列?

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多・斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为 “兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、…… 在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

既然已知递归方法定义,那么 Java 的代码实现怎么去完成呢?

1
2
3
4
5
6
7
8
public static void main(String[] args) {
System.out.println(fibonacciSequence(20));
}
private static long fibonacciSequence(int f){
if (f == 0) return 0;
if (f == 1) return 1;
return fibonacciSequence(f - 1) + fibonacciSequence(f - 2);
}