使用命令行工具

  • 主要的工具
    • javac 编译
    • java 运行(控制台及图形界面程序)
    • javaw 运行图形界面程序
    • appletViewer 运行applet程序
  • 另外常用的几个工具
    • jar 打包工具
    • javadoc 生成文档
    • Javap 常看类信息及反汇编

使用package时的编译

  • 文件及路径一致

  • 程序中使用package语句

  • 使用import语句

  • 编译及运行

    假设文件路径为:

    则 javac -d classes src\edu\pku\tds\ui\*.java src\edu\pku\tds\util\*.java src\edu\pku\tds\*.java

    ​ java -cp classes edu.pku.tds.PackageTest

使用jar打包

  1. 编译 javac A.java
  2. 打包 jar cvfm A.jar A.man A.class
    • c表示创建(create),v表示显示详情(verbose),f表示指定文件名,m表示清单文件
    • 其中A.man是清单文件(manifest),内容如下(清单文件可以任意命名,常见的是用MANIFEST.MF):
      • Manifest-Version: 1.0
      • Class-Path: .
      • Main-Class: A
  3. 运行 java -jar A.jar

Java数据类型

Java是一种强类型语言。每个变量都必须要有一种数据类型。Java一共有八种基本的数据类型。其中有4种整型,2中浮点型,一种用于表示Unicode编码的字符单元的字符类型char类型,和一种用于表示真值的boolean类型。(注意:String不是基本数据类型

整型

整型用于表示没有小数部分的数值,它允许是负数。类型如下:

类型 字节大小 取值范围
byte 1字节 -128 ~ 127
short 2字节 -32 768 ~ 32 767(3万左右)
int 4字节 -2 147 483 648 ~ 2 147 483 647(20亿左右)
long 8字节 -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807

此外,可以用不同的前后缀来表示不同的进制:

  • 0x 前缀表示十六进制数值
  • 0 前缀表示八进制数值
  • 0b 前缀表示二进制数值

在书写较大的数字时,可以用_来分割,例如 0b000_111_010_100 表示468,Java编译器回去除这些下划线。

注意:在c和c++中,int 和 long 常因处理器位数不同而占用不同的字节数。但是在Java中所有的数据类型所占的字节数量与平台无关。另外,Java中没有任何无符号类型(unsigned)。

浮点类型

浮点类型用于表示有小数部分的数值,类型如下:

类型 字节数 取值范围
float 4 [-3.40282346638528860e+38 , -1.40129846432481707e-45] ∪ [1.40129846432481707e-45 ~ 3.40282346638528860e+38]
double 8 [-1.79769313486231570e+308,-4.94065645841246544e-324] ∪ [4.94065645841246544e-324,1.79769313486231570e+308]

后缀F表示float类型,后缀D表示double类型,当没有后缀时,默认为double类型。

注意:浮点数值不适用于禁止出现舍入误差的计算中。例如:System.out.println(2.0 - 1.1); 将打印出0.8999999999999999而不是0.9。主要原因是浮点数在系统中采用二进制表示,而在二进制中无法精确地表示 1/10,就像十进制中无法精确地表示 1/3 一样。

char类型

char类型用于表示单个字符。

Unicode编码单元可以表示为十六进制数值。范围为 \u0000 到 \uffff 。例如 \u03C0 表示圆周率 Π。

boolean类型

boolean类型有两个值:true false。整型和布尔类型之间不能相互转换。

注意:在c和c++中,数值和指针可以替代boolean值,0相当于false,非0值相当于true。但是在Java中不能这样。


数组

一维数组

声明及创建

1
2
3
4
5
6
7
8
9
10
11
12
//一维数组的声明
double[] array;

//一维数组的创建
double[] array = new double[10];


//一维数组初始化
int[] array = {2, 3, 5, 7, 11, 13};
//初始化匿名数组
new int[] {17, 19, 23, 29};
//使用这种方式进行初始化时,数组的大小就是初始值的个数

数组的遍历

1
2
3
4
5
6
7
8
9
//for循环
for (int i = 0; i < array.length(); i++) {
System.out.println(array[i]);
}

//增强for循环(for-each循环)
for (int i: array) {
System.out.println(i);
}

数组常用方法(Arrays类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Arrays;

//1.打印数组时使用,返回String类型
Arrays.toString(array);

//2.从小到大排序
Arrays.sort(array);

//3.二分法查找元素
Arrays.binarySearch(array, 11);

//4.判断数组是否相等
Arrays.equals(array1, array2);

//5.填充元素
Arrays.fill(array, 11);

//6.数组元素的复制
Integer[] newArray = Arrays.copyOf(array, array.length);

//7.将一组值转化为list
List<Integer> asList = Arrays.asList(1, 2, 3, 4, 5);

多维数组

声明及创建

1
2
3
4
5
6
7
8
//声明
int[][] array;

//创建
int[][] array = new int[10][10];

//初始化
int[][] array = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
//普通for循环
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]);
}
}

//增强型for循环(for-each)
for (int[] interval : array) {
for (int i : interval) {
System.out.println(i);
}
}

若要快速打印二维数组的所有元素,也可以使用Arrays类中的deepToString方法(返回String类型元素):

1
Arrays.deepToString(array);

不规则数组

Java其实没有多维数组,只有一维数组,多维数组其实就是数组的数组。例如 int[][] array = new int[10][10]; 既不同于 C 中 int array[10][10]; 也不同于 int (*array)[6] = new int[10][10]; 这正是 Java 的优势所在。

基于这个优势,Java 可以很方便的对两行进行交换:

1
2
3
int[] temp = array[i];
array[i] = array[j];
array[j] = temp;

在构造数组时,也可以给数组每行不同的空间大小:

1
2
3
4
5
int[][] array = new int[4][];
array[0] = new int[4];
array[1] = new int[3];
array[2] = new int[2];
array[3] = new int[1];

Java对象和类

类的五大成员:属性、方法、构造器、代码块、内部类

定义

完整的类定义

1
2
3
类声明
成员变量声明(可有多个)
方法定义及事项(可有多个)
1
2
3
4
5
6
[public][abstract | final] class className [extends superclassName] [implements InterfaceNameList]{
[public | protected | private][static][final][transient][volatile] type variableName
[public | protected | private][static][final | abstract][native][synchronized] returnType methodName ([paramList]) [throws exceptionList]{
statement;
}
}

完整的Java源文件

源文件的名称必须与属性为 public 的类的类名完全相同

在一个.java 文件中,package 语句和 public 类最多只能有一个

1
2
3
4
指定文件中的类所在的包,0个或1
指定引入的类,0个或多个
属性为public的类定义,0个或1
接口或类定义,0个或多个
1
2
3
4
package packageName;
import packageName.[className | *];
public classDefinition
interfaceDefinition and classDefinition

常用访问修饰符

Java中有四种常用的访问修饰符

  • default(默认,即什么也不写的时候就自动加上):在同一包内可见。可以使用的对象:类、接口、变量、方法。
  • private:在同一类中可见。可以使用的对象:变量、方法、内部类。
  • public:对所有类可见。可以使用的对象:类、接口、变量、方法。
  • protected:对同一包内的类和所有子类可见。可以使用的对象:变量、方法、内部类。

访问权限如下表:

修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
default Y Y Y N N
private Y N N N N
protected Y Y Y Y/N N
public Y Y Y Y Y

对于 protected :

  • 基类的 protected 是包内可见的,并且子类也是可见的
  • 若子类和基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问 基类的 对象实例中的 受protected约束的方法

继承

this 和 super

this 关键字的作用:

  • this 调用本类中的属性和方法
  • this 调用本类中的其他构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Father {
private String name;
private int age;
private char sex;

public Father() { }

public Father(String name) {
this();
this.name = name;
}

public Father(String name, int age) {
this(name);
this.age = age;
}

public Father(String name, int age, char sex) {
this(name, age);
this.sex = sex;
}

public void test() {
this.check();
}

public void check() { }
}

super 关键字的作用:

  • super 调用基类中的非私有属性和方法
  • super 调用父类中的构造器,在构造函数中使用super时,super只能放在第一句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son extends Father {
private String name;

public Son() {
super();
}

public Son(String name) {
super(name);
}

void check01() {
super.check();
super.test();
}
}

注意:虽然 super 和 this 用法相似,但是他们并不是一个概念。this 实际上是对当前对象的引用,而 super 并不是对一个对象的引用,他只是一个指示编译器调用超类方法的特殊关键字。

1
2
Father father = this; //right
Sun sun = super; //error

方法重写(override)

注意事项和使用细节:

方法重写也叫方法覆盖,需要满足下列条件

  1. 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。
  2. 子类方法的返回类型和父类方法返回类型一样,或是父类返回类型的子类。比如父类方法返回类型是Object,子类方法返回类型是String。
  3. 子类方法不能缩小父类方法的访问权限。

多态

类的多态

  1. 一个对象的编译类型可以和运行类型不一致。
  2. 编译类型在创建时就确定了,不能改变。
  3. 运行类型可以变化。
  4. =左边是编译类型,=右边是运行类型。

可以用二元运算符 instanceof 判断运行类型。

动态绑定

Java 具有动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性的时候,没有动态绑定机制,哪里声明就在哪里使用

即:方法有动态绑定机制,属性没有动态绑定机制

抽象类

抽象类使用 abstract 关键词修饰,它的基本功能依旧存在,成员变量、成员方法和构造方法的访问方式和普通类一样。抽象类不能实例化对象,必须被继承才能被使用。包含抽象方法的类就是抽象类。

书写格式:

1
2
3
4
5
6
7
8
//抽象类格式
public abstract class 类名{

}

//抽象方法格式
public abstract 返回值类型 方法名(参数);
public abstract void eat(int num);

Java常用类

枚举类enumeration(enum)

枚举是一组常量的集合。也就是说枚举属于一种特殊的类,里面只包含一组有限的特定的对象。

自定义枚举类

实现方法:

  1. 将构造器私有化,防止直接new出来
  2. 去掉set方法,防止属性被修改
  3. 直接在内部创建固定的对象
  4. 对外暴露对象(通过为对象添加 public static 修饰符)
  5. 优化:加入一个final修饰符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Season.java
public class Season {
private String name;
private String desc;

private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}

public static final Season SPRING = new Season("春天", "温暖");
public static final Season SUMMER = new Season("夏天", "炎热");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season WINTER = new Season("冬天", "寒冷");

public String getName() {
return name;
}

public String getDesc() {
return desc;
}

@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

//使用此枚举类
public static void main(String[] args) {
System.out.println(Season.SPRING);
}

使用enum关键字生成枚举类

实现方法:

  1. 使用关键字enum替代class
  2. public static final Season SPRING = new Season(“春天”, “温暖”); -> SPRING(“春天”, “温暖”); 即:常量名(实参列表);
  3. 如果有多个常量(对象),使用逗号间隔,最后一个用分号
  4. 定义的常量对象必须写在最前面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Season01.java
public enum Season01 {
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");

private String name;
private String desc;

private Season01(String name, String desc) {
this.name = name;
this.desc = desc;
}

public String getName() {
return name;
}

public String getDesc() {
return desc;
}

@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

//使用此枚举类
public static void main(String[] args) {
System.out.println(Season01.SPRING);
}

enum常用方法

方法名 详细描述
valueOf 将字符串转化为枚举对象,要求字符串必须为已有常量名,否则报异常
toString Enum类已经重写该方法了,返回的是当前对象名。子类可以重写该方法,用来返回对象的属性信息
equals 类似于二元运算符 ==
getDeclaringClass 得到枚举常量所属的枚举类型
name 返回当前对象名(常量名),子类不能重写
ordinal 返回当前对象的位置号(默认从0开始)
compareTo 比较两个枚举常量,比较的就是位置号
clone 枚举类不能被clone。Enum类实现了一个仅抛出异常的不变clone(),防止子类实现克隆方法。
values 返回当前枚举类中的所有常量

String类

说明:

  1. String 对象用于保存字符串,也就是一组字符序列
  2. 字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节大小
  3. String 类有很多构造器,常用的有:
    • String str = new String();
    • String str = new String(String original);
    • String str = new String(char[] a);
    • String str = new String(char[] a, int startIndex, int count);
    • String str = new String(byte[] b);
  4. String 类实现了 Serializable 接口(可以串行化:可以在网络上传输,可以保存到文件)
  5. String 类实现了 Comparable 接口(String 对象可以比较大小)
  6. String 是 final 类,不能被其他的类继承
  7. String 内部实现时:private final char value[]; 存储时本质就是不可变字符数组

声明和创建

1
2
3
4
5
6
//声明
String str;

//创建
1. String str = "Hello, world!";
2. String str = new String("Hello, world!");

两种字符串创建方式的区别:

方式一:先从常量池查看是否有”Hello, world!”数据空间,如果有,则直接指向;如果没有,则重新创建然后指向。所以str最终指向的是常量池的空间地址。相当于值传递。

方式二:先在堆中创建空间,里面维护了value属性,指向常量池的”Hello, world!”数据空间。如果常量池没有”Hello, world!”,则重新创建。所以使用这种方法,实际上是创建了两个对象。相当于引用。

String类常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
字符串的比较:
matches
判断字符串与指定正则表达式是否相匹配,如:
String a = "Hello";
String re = ".*?";
System.out.println(a.matches(re));
equals
判断字符串在堆中是否相等
compareTo
比较两个字符串的大小
前面的参数大返回1
后边的参数大则返回-1
相等返回0
regionMatches
比较两个字符串的特定区域是否相等
参数:字符串1.regionMathes((是否忽略大小写boolean,)串1起始下标,字符串2,串2起始下标,比较长度);
startsWith
检测字符串是否以指定前缀开始
endsWith
检测字符串是否以指定后缀结束
equalsIgnoreCase
忽略大小写
compareToIgnoreCase
忽略大小写
获取字符串
length()
charAt()
用于返回指定索引的字符
str1 = str1.concat(str2);
将str2连接到str1上,并返回连接后的字符串
substring
返回指定字符串的子串
参数:str1.substring(起始下标,(终止下标));
在原字符串的基础上创建新字符串(不改变原字符串),返回String类型
toLowerCase
将字符串变为小写
toUpperCase
将字符串变为大写
trim
删除字符串的头尾空白符
replace
替换原字符串中的特定字符/字符串
参数:str1.replace(old, new);
replaceFirst
替换原字符串第一个匹配给定正则表达式的子字符串
参数:str1.replaceFirst(正则表达式,替换的串);
匹配失败返回原字符串
replaceAll
替换原字符串所有匹配给定正则表达式的子字符串
split
根据给定的正则表达式拆分字符串,返回字符串数组类型
参数:str1.split(正则表达式(,要拆分的份数));
从前往后拆分
找出某个字符串或者字串(以下两个方法方法重载方法较多)
indexOf
参数:str1.indexOf(char/String(,fromIndex));
返回指定字符/字符串(从fromIndex位置开始)在母串第一次出现处的索引,如果没有,返回-1
lastIndexOf
返回指定字符/字符串在母串最后一次出现处的索引,如果没有,返回-1。如果有fromIndex参数,则从此位置反向索引
字符串与数组之间的转化
串转化为数组
getBytes((String charsetName));
使用特定的字符集将字符串编码为byte序列,并将结果存储到一个新的byte数组中。如果不写参数,会使用平台默认的字符集
toCharArray
直接返回字符串转换而成的字符数组
参数:str1.toCharArray();
getChars
参数:str1.getChars(要复制的第一个字符的下标,要复制的最后一个字符的下标,目标字符数组,目标字符数组的起始下标);
没有返回值,但会抛出IndexOutOfBoundsException异常
数字转化为串
new String(char[])
将字符数组转换为字符串并返回
String.valueOf(Object o);
将传进去的int/long/float/double/Object/char/char[]/boolean类型转换为String类型并返回
格式化字符串
format
如:System.out.format("%s", str1);等同于 System.out.printf("%s", str1);

StringBuilder 和 StringBuffer 类

String 中的内容是不可变的,当需要对字符串中的内容做修改时,应使用StringBuilder和StringBuffer类。(上述方法提到的修改String内容的方法实际上是创建了新对象,使用StringBuilder和StringBuffer修改时不会产生新对象)

区别

区别:StringBuilder 类是在 Java5 之后才出现的。StringBuilder 的方法是线程不安全的(不能同步访问),但是它的效率更高。

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
StringBuilder类/StringBuffer类
append
将指定字符插入到原字符的末尾
insert
参数:str1.insert(offset, str);
将字符串str插入到原字符串str1的下标为offset后的位置
delete
参数:str1.delete(start, end);
删除str1从start到end的内容(前闭后开)
reverse
将字符串反转
replace
参数:str1.replace(start, end, str);
用str替换str1中从start到end之间的内容(前闭后开)
setCharAt
参数:str1.setCharAt(index, char);
将下标为index的内容更改为char
几个常见方法
toString
转为字符串
capacity
返回当前容量
ensureCapacity(int minimumCapacity)
确保容量至少等于指定的最小值
charAt
返回指定下标元素
length()
返回字符串长度
setLength
设置字符序列长度
substring
参数:str1.substring(start(, end));
返回从start到end之间的内容(end可有可无)
trimToSize
将动态数组的容量调整为数组的个数

String, StringBuilder, StringBuffer 对比

  1. String 不可变序列,效率低,但是复用率高,适合在不对字符串修改时使用
  2. StringBuffer 可变序列,效率较高,但是线程安全,多线程操作时是首选
  3. StringBuilder 可变序列,效率最高,但是线程不安全,只能在单线程程序中使用

日期和时间

Calendar 类说明:

  1. Calendar 是一个抽象类,并且构造器是 private
  2. 可以通过 getInstance() 来获取实例
  3. Calendar 提供了大量的方法和字段给程序员
  4. Calendar 没有提供对应的格式化的类,因此需要程序员自己组合来输出
  5. 如果要按照 24 小时进制来获取时间,Calendar.HOUR ==改成==> Calendar.HOUR_OF_DAY
1
2
3
4
5
6
7
8
9
//获取时间
Calendar c = Calendar.getInstance();

System.out.println("年" + c.get(Calendar.YEAR));
c.get(Calendar.MONTH)+1;
c.get(Calendar.DAY_OF_MONTH);
c.get(Calendar.HOUR);
c.get(Calendar.MINUTE);
c.get(Calendar.SECOND);

注意:Calendar 返回月份时,默认是从0开始,所以要加1

前两代日期类的不足的分析:

JDK1.0 中包含了一个 java.util.Date 类,但是它的大多数方法已经在 JDK1.1 引入 Calendar 类之后被弃用了。而 Calendar 也存在的问题是:

  1. 可变性:像日期和时间等这样的类应该是不可变的。
  2. 偏移性:Date 中的年份是从 1900 开始的,而月份都从 0 开始。
  3. 格式化:格式化只对 Date 有用,Calendar 则不行。
  4. 此外,他们也是线程不安全的;不能处理闰秒等(每隔2天,多出1秒)。

第三代日期常用方法(JDK8 加入)

  1. LocalDate (日期==年月日)
  2. LocalTime (时间==时分秒)
  3. LocalDateTime (日期时间==年月日时分秒)
1
2
3
4
5
6
7
8
9
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);//格式为:2022-01-05T11:46:15.304429400
System.out.println(ldt.getYear());
System.out.println(ldt.getMonth());//英文显示的月份
System.out.println(ldt.getMonthValue());//数字显示的月份
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());

可以使用 DateTimeFormatter 类对日期进行格式化,代码如下:

1
2
3
4
5
LocalDateTime ldt = LocalDateTime.now();

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
System.out.println(format);

其他

Instant 时间戳:

1
2
3
4
5
6
7
8
9
//1. 通过静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);

//2. 通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);

//3. 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant();

还有一系列的 plus 和 minus 方法可以对日期进行加减操作

1
2
3
4
5
6
7
//看看 890 天后是什么时候
LocalDateTime localDateTime = ldt.plusDays(890);
System.out.println("890天后 = " + dateTimeFormatter.format(localDateTime));

//看看 3456 分钟前是什么时候
LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
System.out.println("3456分钟前 = " + dateTimeFormatter.format(localDateTime2));

常用包装类

Java 为每一个内置数据类型提供了对应的包装类。所有的包装类(Integer, Long, Byte, Double, Float, Short)都是抽象类 Number 的子类。

对应关系如下:

包装类 基本数据类型
Boolean boolean
Byte byte
Short short
Integer int
Long long
Character char
Float float
Double double

继承关系如下:

img

从内置类型到包装类叫做装箱,从包装类对象到内置类型叫做拆箱。

只有变成了包装类才能使用类中的方法,而且一般编译器会自动进行装箱、拆箱的过程。

除 Character 外,字符串都可以通过 parseXXX(String str) 的方法转化为基本数据类型

Math数学类

Math 类常用方法(Math 类的方法都为 static 形式,可以直接使用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1. 三角函数方法
double sin(double radians)
double cos(double radians)
double tan(double radians)
double toRadians(double degrees)
double toDegrees(double radians)
double asin(double a)
double acos(double a)
double atan(double a)
2. 指数函数方法
double exp(double x) e的x次方
double log(double x) ln(x)
double log10(double x) lg(x)
double pow(double a,double b) a的b次方
double sqrt(double x) x的平方根
3. 服务方法
取整方法
double ceil(double x) //向上取整
double floor(double x) //向下取整
double rint(double x) //向最近的取整
int round(float x) //四舍五入
long round(double x) //四舍五入
min, max, abs方法
random方法
4. double型常量:Math.PI(圆周率) Math.E(自然对数的底数)

System 类

常用方法:

  1. exit 退出当前程序
  2. arrayCopy 复制数组元素,比较适合底层调用。一般使用 Arrays.copyOf 完成复制数组。
1
2
3
int[] source = {1, 2, 3};
int destination = new int[3];
System.arrayCopy(source, 0, destination, 0, 3);
  1. currentTimeMillens 返回当前时间距离 1970-1-1 的毫秒数
  2. gc 运行垃圾回收机制 System.gc();

Java接口

接口实际上是一种规范。实现接口,是对 Java 单继承的一种补充。接口也具有多态性。

注意事项:

  1. 接口不能被实例化
  2. 接口中的所有方法都是 public 类型,接口中的抽象方法可以不用 abstract 修饰
  3. 一个普通类实现接口就必须将接口中的所有方法实现
  4. 抽象类去实现接口时,可以不实现接口的抽象方法
  5. 一个类可以实现多个接口
  6. 接口中的属性,只能是 final,而且是 public static final 修饰符
  7. 接口中属性的访问形式:接口名.属性名
  8. 接口不能继承其他的类,但是可以继承多个别的接口
  9. 接口的修饰符只能是 public 和默认,和类的修饰符一样

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//camera.java
package com.pushihao.接口;

public class camera implements machine {

@Override
public void work() {
System.out.println("the camera is working");
}

@Override
public void stop() {
System.out.println("the camera is stopping");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//computer.java
package com.pushihao.接口;

public class computer implements machine{
@Override
public void work() {
System.out.println("the computer is working");
}

@Override
public void stop() {
System.out.println("the computer is stopping");
}

public void play() {
System.out.println("the computer can play games");
}
}
1
2
3
4
5
6
7
//machine.java
package com.pushihao.接口;

public interface machine {
void work();
void stop();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//interface.java
package com.pushihao.接口;

public class Interface {
public static void main(String[] args){
machine[] machines = new machine[2];
machines[0] = new camera();
machines[1] = new computer();

for (machine i:machines){
i.work();
i.stop();

//instanceof判断运行类型
if (i instanceof computer){
((computer) i).play();
}
}
}
}

Java动态代理

动态代理有两种实现方式:

  • JDK 动态代理(重要)
  • CGLIB 动态代理

CGLIB(了解)

使用继承机制,通过在子类中重写同名方法完成对父类的代理

其要求被代理的类不能为 final,方法也不能为 final

要求较宽松,只要类能被继承就能使用此方法,效率相对较高

JDK 动态代理

重要的类和方法

Proxy 类:用于创建代理对象的类,其中有很多静态方法

静态方法:newProxyInstance

1
2
3
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

参数:

  • ClassLoader loader 要代理的目标对象的类加载器,负责向内存中加载对象,可以通过反射获取
  • Class<?>[] interfaces 目标对象实现的接口
  • InvocationHandler h 我们自己写的,代理类要完成的功能

返回值:

  • 代理对象

实现动态代理的步骤

  1. 创建接口,定义目标类要完成的功能
  2. 创建目标类实现接口
  3. 创建 InvocationHandler 接口的实现类,在 invoke 方法中完成代理类的功能
    • 调用目标方法
    • 增强功能
  4. 使用 Proxy 类的静态方法,创建代理对象,并返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//可以用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

//被代理的接口
private Object target;

public void setTarget(Object target) {
this.target = target;
}

//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

//利用反射机制,处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}

反射

  1. 反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性以及方法。反射在设计模式和框架底层都会用到。
  2. 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。可以通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。

在反射中,可以把方法视为对象

Java 程序在计算机执行有三个阶段

img

反射相关的主要类

  1. java.lang.Class 代表一个类,Class 对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method 代表类的方法,Method 对象表示某个类的方法
  3. java.lang.reflect.Field 代表类的成员变量,Field 对象表示每个类的成员变量
  4. java.lang.reflect.Constructor 代表类的构造方法,Constructor 对象表示构造器

这些类在 java.lang.reflection

Class 类

基本介绍

  1. Class 也是类,因此也继承 Object 类
  2. Class 类对象不是 new 出来的,而是系统创建的
  3. 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
  4. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  5. 通过 Class 可以完整地得到一个类的完整结构,通过一系列API
  6. Class 对象是存放在堆中的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)

Class 类的常用方法

如下表:

方法名 功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象
Object newInstance() 调用缺省构造函数,返回该 Class 对象的一个实例
getName() 返回此 Class 对象所表示的实体(类、接口、数组类、基本类型等)名称
Class getSuperClass() 返回当前 Class 对象的父类的 Class 对象
Class[] getInterfaces() 返回当前 Class 对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示 此 Class 所表示的实体 的超类的 Class
Constructor[] getConstructors() 返回一个包含某些 Constructor 对象的数组
Field[] getDeclaredFields() 返回 Field 对象的一个数组
Method getMethod(String name, Class … paramTypes) 返回一个 Method 对象,此对象的形参类型为 paramType

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static void main(String[] args) throws Exception {
String classPath = "com.pushihao.Reflection_.Car";

//任何对象都可以通过getName()获取名字
//1. 获取car类对应的Class对象
Class<?> cls = Class.forName(classPath);

//2. 输出cls
System.out.println("=================================");
System.out.println(cls); // 显示cls对象是哪个类的Class对象
System.out.println(cls.getClass()); // 输出cls本身的运行类型

//3. 得到包名
System.out.println("=================================");
System.out.println(cls.getPackage().getName());

//4. 得到全类名
System.out.println("=================================");
System.out.println(cls.getName());

//5. 通过Class对象创建对象实例
System.out.println("=================================");
Car car = (Car)cls.getConstructor().newInstance();
System.out.println(car);

//6. 通过反射获取属性 name
System.out.println("=================================");
Field name = cls.getField("name");
System.out.println(name.get(car));

//7. 通过反射给属性赋值
System.out.println("=================================");
name.set(car, "奔驰");
System.out.println(name.get(car));

//8. 获取类中所有的属性
System.out.println("=================================");
Field[] fields = cls.getFields();
for (Field f: fields) {
System.out.println(f.getName());
}
}

获取 Class 类对象的常用方法

  1. 前提:已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException,示例:Class cls1 = Class.forName(“com.pushihao.Cat”);

应用场景:多用于配置文件,读取类全路径,加载类

  1. 前提:若已知具体的类,通过类的 class 获取,该方式最为安全可靠,程序性能最高,示例:Class cls2 = Cat.class;

应用场景:多用于参数传递,比如通过反射得到对应构造器对象

  1. 前提:若已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象,实例:Class cls3 = 对象.getClass();

应用场景:通过创建好的对象,获取 Class 对象

  1. 其他方式,示例:
1
2
ClassLoader cl = 对象.getClass().getClassLoader();
Class cls4 = cl.loadClass("com.pushihao.Cat");
  1. 基本数据(int, char, boolean, float, double, byte, long, short)可通过.class得到 Class 类对象:Class cls5 = 基本数据类型.class;
  2. 基本数据类型对应的包装类,可以通过.TYPE得到 Class 类对象:Class cls6 = 包装类.TYPE;

类加载

基本说明:

反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:编译时就加载相关的类,如果没有此类就报错(不管用到没用到),依赖性太强
  2. 动态加载:运行时才加载需要的类,如果运行时没有用到该类,即使不存在也不报错,降低了依赖性

类加载时机:

前三个都是静态加载,只有4是动态加载

  1. 当创建对象时(new)
  2. 当子类被加载时
  3. 调用类中的静态成员时
  4. 通过反射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//假设没有Dog类,也没有Person类
public class Demo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
switch (n) {
case 1:
Dog dog = new Dog();//静态加载,虽然不一定用到,但是会报错
break;
case 2:
Class.forName("Person");//动态加载,如果n!=2就不报错,因为它不会加载
break;
case 3:
System.out.println("Do nothing!");
break;
}
}
}

类加载阶段:

加载 -> 准备(验证 -> 准备 -> 解析)-> 初始化

通过反射获取类的结构信息

java.lang.Class 类

  1. getName 获取全类名
  2. getSimpleName 获取简单类名
  3. getFields 获取所有public修饰的属性,包含本类以及父类的
  4. getDeclaredFields 获取本类中所有属性
  5. getMethods 获取所有public修饰的方法,包含本类以及父类的
  6. getDeclaredMethods 获取本类中所有方法
  7. getConstructors 获取所有public修饰的构造器,只包含本类
  8. getDeclaredConstructors 获取本类中所有构造器
  9. getPackage 以Package形式放回包信息
  10. getSuperClass 以Class形式返回父类信息
  11. getInterfaces 以Class[]形式返回接口信息
  12. getAnnotations 以Annotation[]形式返回所有注解信息

java.lang.reflect.Field 类

  1. getModifiers 以int形式返回修饰符

    说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果

  2. getType 以Class形式返回类型

  3. getName 返回属性名

java.lang.reflect.Method 类

  1. getModifiers 以int形式返回修饰符

    说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果

  2. getReturnType 以Class形式获取返回类型

  3. getName 返回方法名

  4. getParameterTypes 以Class[]形式返回参数类型的数组

java.lang.reflect.Constructor 类

  1. getModifiers 以int形式返回修饰符
  2. getName 返回构造器名(全类名)
  3. getParameterTypes 以Class[]形式返回参数类型数组

反射暴破

假设有这么一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.pushihao.Reflection_.Explode;

@SuppressWarnings({"all"})
public class Person {
public String name = "Pillage";
private Integer age = 19;
private static Double salary = 100.0d;

public Person() { }
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
private Person(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}

public void Hi() {
System.out.println(name + ":hi~~");
}

private String UpSalary(Double salary) {
return "salary up to:" + salary;
}

private static String staticUpSalary(String name, Double salary) {
return name + "'s salary up to:" + salary;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}

通过反射创建对象

  1. 方式一:调用类中的 public 修饰的无参构造器 class 对象
  2. 方式二:调用类中的指定构造器
  3. Class 类相关方法
    • newInstance:调用类中的无参构造器,获取对应类的对象
    • getConstructor:根据参数列表,获取对应的构造器对象
    • getDecalaredConstructor:根据参数列表,获取对应的构造器对象
  4. Constructor 类相关方法
    • setAccessible:爆破(暴力破解:反射可以使用类中的私有构造器/方法)
    • newInstance:调用构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");

//调用 public 无参构造器
Object o1 = cls.newInstance();
System.out.println(o1);

//调用 public 有参构造器
Constructor<?> constructor1 = cls.getConstructor(String.class, Integer.class);
Object o2 = constructor1.newInstance("Daisy", 18);
System.out.println(o2);

//调用非 public 有参构造器
Constructor<?> constructor2 = cls.getDeclaredConstructor(String.class, Integer.class, Double.class);
constructor2.setAccessible(true);//破解私有构造器
Object o3 = constructor2.newInstance("Jane", 18, 10000d);
System.out.println(o3);
}

通过反射访问类中的属性

  1. 根据属性名获取 Field 对象
1
Field f = class对象.getDeclaredField(属性名);
  1. 暴破
1
f.setAccessible(true);
  1. 访问
1
2
f.set(对象实例, 新值);
f.get(对象实例);

如果是静态属性,则 set 和 get 中的参数可以写成 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");
Person person = (Person) cls.getConstructor().newInstance();

//获取 public 类型的 name 属性
Field name = cls.getField("name");
System.out.println(name.get(person));
name.set(person, "Daisy");
System.out.println(person);

//获取非 public 类型的 age 属性
Field age = cls.getDeclaredField("age");
age.setAccessible(true);//暴破
System.out.println(age.get(person));
age.set(person, 66);
System.out.println(person);

//获取静态的非 public 类型的 salary 属性
Field salary = cls.getDeclaredField("salary");
salary.setAccessible(true);
System.out.println(salary.get(person));
salary.set(null, 90d);
System.out.println(salary.get(null));
}

通过反射访问类中的方法

  1. 根据方法名和参数列表获取 Method 方法对象
1
2
Method method = class对象.getMethod(方法名, class对象);
Method method = class对象.getDeclaredMethod(方法名, class对象);
  1. 获取对象实例
1
Object o = class对象.getConstructor().newInstance();
  1. 暴破
1
method.setAccessible(true);
  1. 访问
1
Object returnValue = method.invoke(o, 实参列表);

如果是静态方法,则 invoke 的参数 o 可以写成 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");
Object o = cls.getConstructor().newInstance();

//调用 public 的 hi 方法
Method hi = cls.getMethod("Hi");
hi.invoke(o);

//调用非 public 的 UpSalary 方法
Method upSalary = cls.getDeclaredMethod("UpSalary", Double.class);
upSalary.setAccessible(true);
Object sal = upSalary.invoke(o, 100.0d);
System.out.println(sal);

//调用静态的非 public 的 staticUpSalary 方法
Method staticUpSalary = cls.getDeclaredMethod("staticUpSalary", String.class, Double.class);
staticUpSalary.setAccessible(true);
System.out.println(staticUpSalary.invoke(null, "Daisy", 666d));
}

Java集合

集合框架图如下:

img

img

img

大致可以看出,Java 集合框架主要包含两种容器。一种是存储元素集合的 Collection,另一种是存储键值对的 Map。

总结:

  1. 集合主要分为两组(单列集合,双列集合)
  2. Collection 接口有两个重要的子接口 List 和 Set,他们实现的子类都是单列集合
  3. Map 接口实现的子类则是双列集合,存放K-V键值对

Collection 接口

Collection接口常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
add
参数:array.add(Object o);
添加元素

remove
参数:array.remove(Object o);
删除指定元素

contains
参数:array.contains(Object o);
查找元素是否存在

size
获取元素个数

isEmpty
判断是否为空

clear
清空

addAll
参数:array.addAll(Collection c);
将另一个集合添加(添加多个元素)

containsAll
参数:array.containsAll(Collection c);
查找多个元素是否都存在

removeAll
参数:array.removeAll(Collection c);
删除多个元素

使用迭代器遍历 Collection

说明:

  1. Iterator 对象称为迭代器,主要用于遍历 Collection 集合中的元素
  2. 所有实现了 Collection 接口的集合类都有一个 iterator() 方法,用以返回一个实现了 iterator 接口的对象,即可以返回一个迭代器
  3. Iterator 仅用于集合的遍历,Iterator 本身并不存放对象
1
2
3
4
5
Iterator iterator = array.iterator();
while (iterator.hasNext()) {
String str = (String) iterator.next();
System.out.println(str);
}

List 接口

List 接口基本介绍
  1. List 集合类中的元素有序,并且可以重复
  2. List 集合中的每个元素都有其相应的顺序索引,即支持索引
  3. List 容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取相应元素
List 接口常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void add(int index, Object o);
在index位置插入o元素

boolean addAll(int index, Collection c);
从index位置插入c集合

Object get(int index);
获取index下标的元素

int indexOf(Object o);
返回o在集合中首次出现的位置

int lastIndexOf(Object o);
返回o在集合中最后一次出现的位置

Object remove(int index);
移出index下标位置的元素,并返回此元素

Object set(int index, Object e);
设置指定index位置的元素为e(替换)

List subList(int fromIndex, int toIndex);
返回从fromIndex下标到toIndex下标位置的子元素集合(前闭后开)
Vector、ArrayList 与 LinkedList 比较
  1. Vector 是线程安全的,但效率相对较低,所以在多线程时应使用 Vector
  2. ArrayList 和 Vector 都是以 Object 数组来存储的。在存储时如果数据个数超出了内部数组当前的长度,Vector 会自动增长为原数组大小的一倍,而 ArrayList 是50%,所以在存放大量数据时应使用 Vector 更有优势
  3. ArrayList 是基于动态数组来实现,而 LinkedList 是基于链表来实现。所以当需要大量随机访问元素时,ArrayList 是首选,而当需要大量的插入或删除元素时,LinkedList 是首选。(类比数组与链表)

Set接口

Set 接口实现了 Collection 接口,所以 Collection 的方法 Set 接口的实现类也能用

List 接口与 Set 接口对比

  1. Set 接口存储的数据是无序的,并且不能重复。而 List 接口实例存储的是有序的,可以重复的元素
  2. Set 检索效率较低,但是插入和删除效率高(因为插入和删除不会引起其他元素的位置的改变(内部实现其实还是哈希表与红黑树 HashSet、TreeSet))
  3. List 查找效率高,但是插入和删除效率较低(因为会引起其他元素位置的改变)

Map 接口

说明

Map 接口的常用实现类:HashMap、HashTable 和 Properties

HashMap 说明
  1. HashMap 是 Map 接口使用频率最高的实现类
  2. HashMap 是以 Key-Value 键值对的方式来存放数据
  3. Key 不能重复,但是 Value 可以重复
  4. 允许使用 null 键和 null 值
  5. 如果添加相同的 Key,则会覆盖原来的 Key-Value,相当于是修改(Key 不会替换,Value 会替换)
  6. 元素是无序的,底层是以哈希表的方式来存储的
  7. HashMap 没有实现同步,因此是线程不安全的
HashTable 说明
  1. 存放的元素是键值对
  2. HashTable 的键和值都不能为 null,否则抛出 NullPointerException
  3. HashTable 是线程安全的,而 HashMap 是线程不安全的

Map 常用操作

增删改查等操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Map map = new HashMap();

//1. put:添加
map.put("高一", "马");
map.put("高二", "朱");
map.put("高三", "张");
System.out.println(map);

//2. remove:根据键删除映射关系
map.remove("高一");
System.out.println(map);

//3. get:根据键获取值
System.out.println(map.get("高二"));

//4. size:获取元素个数
System.out.println(map.size());

//5. isEmpty:判断个数是否为0
System.out.println(map.isEmpty());

//6. containsKey:查找键是否存在
System.out.println(map.containsKey("高三"));

//7. 查找值是否存在
System.out.println(map.containsValue("张"));

//8. clear:清除
map.clear();
System.out.println(map);
遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//注:x.1 都是使用for-each,x.2 都是使用迭代器
//=======第一种遍历方式,先取出所有的键,再根据键获取值(keySet)=========
Set keyset = map.keySet();

System.out.println("========第1.1种方式========");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}

System.out.println("========第1.2种方式========");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}


//=======第二种遍历方式,只遍历值(用的不多)=========
System.out.println("========第2.1种方式========");
Collection values = map.values();
for (Object value : values) {
System.out.println(value);
}

System.out.println("=========第2.2种方式========");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}


//=======第三种遍历方式,通过遍历entrySet,然后转为Map.Entry,调用get========
System.out.println("==========第3.1种方式=======");
Set entrySet = map.entrySet();
for(Object entry:entrySet) {
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}

System.out.println("==========第3.2种方式========");
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Map.Entry m1 = (Map.Entry) iterator2.next();
System.out.println(m1.getKey() + "-" + m1.getValue());
}

开发中如何选择集合实现类

分析如下:

  1. 先判断存储的类型(一组对象或一组键值对)
  2. 一组对象:Collection 接口
    • 允许重复:List
      • 增删多:LinkedList(底层维护了一个双向链表)
      • 改查多:ArrayList(底层维护了一个 Object 类型的可变数组)
    • 不允许重复:Set
      • 无序:HashSet(底层是 HashMap,维护了一个哈希表)
      • 排序:TreeSet
      • 插入和取出顺序一致:LinkedHashSet(底层维护了数组+双向链表)
  3. 一组键值对:Map 接口
    • 键无序:HashMap(底层是哈希表 jdk7:数组+链表 jdk8:数组+链表+红黑树)
    • 键排序:TreeMap
    • 键插入和取出顺序一致:LinkedHashMap
    • 读取文件:Properties

Collections 工具类

工具类介绍

  1. Collections 是一个用来操作 Set、List 和 Map 等集合的工具类
  2. Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

排序操作(均为static方法)

  1. reverse(List); 将List元素反转
  2. shuffle(List); 对List集合元素进行随机排序
  3. sort(List); 根据元素的自然顺序对指定List集合元素按升序排序
  4. sort(List, Comparator); 根据指定的排序方式对List排序
  5. swap(List, int i, int j); 将List集合中下标为 i 的元素与下标为 j 的元素进行交换

查找、替换操作

  1. Object max(Collection); 根据元素的自然顺序(字典序),返回最大元素
  2. Object max(Collection, Comparator); 根据指定顺序(如长度最大等)返回最大元素
    • 如:返回长度最大
    • Object maxObject = Collections.max(list, new Comparator() {
    • ​ @Override
    • ​ public int compare(Object o1, Object o2) {
    • ​ return ((String) o1).length() - ((String) o2).length();
    • ​ }
    • })
  3. Object min(Collection);
  4. Object min(Collection, Comparator);
  5. int frequency(Collection, Object); 返回出现次数最多的元素
  6. void copy(List dest, List src); 将src的内容复制到dest
  7. boolean replaceAll(List list, Object oldVal, Object newVal); 使用新值替换所有旧值

其他操作

创建一个线程安全的集合:

List synchronizedList(List list);

1
private static List<Connection> sqlList = Collections.synchronizedList(new ArrayList<>());

Map接口

Map 接口实现类的特点(JDK8):

  1. Map 与 Collection 并列存在,用于保存具有映射关系的数据:Key-Value
  2. Map 中的 Key 和 Value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中,常用 String 类作为Key
  3. Map 中的 Key 不允许重复,原因和 HashSet 一样
  4. Map 中的 Value 可以重复
  5. Map 的 Key 可以为 null,value 也可以为 null
  6. Key 和 Value 之间存在单向一一对应关系,即通过 Key 总能找到对应的 value
  7. Map 存放数据的 key-value 示意图,一对 k-v 是放在一个 Node 中的,又因为 Node 实现了 Entry 接口,有些书也说一对 k-v 就是一个 Entry

img

img

Map 接口常用方法

img

  1. put
  2. remove
  3. get
  4. size
  5. isEmpty
  6. clear
  7. containsKey