Java基础

Java

基础

image-20220817205659765

如果有多个public,会生成多个class文件(字节码文件)

image-20220817210322306

image-20220817210343765

JDK,JRE

JDK 基本介绍

  1. JDK 的全称(Java Development Kit Java 开发工具包)

​ JDK = JRE + java 的开发工具 [java, javac,javadoc,javap 等]

  1. JDK 是提供给 Java 开发人员使用的,其中包含了 java 的开发工具,也包括了 JRE。所以安装了 JDK,就不用在单独 安装 JRE 了。

JRE 基本介绍

  1. JRE(Java Runtime Environment Java 运行环境)

​ JRE = JVM + Java 的核心类库[类]

  1. 包括 Java 虚拟机(JVM Java Virtual Machine)和 Java 程序所需的核心类库等,如果想要运行一个开发好的 Java 程序, 计算机中只需要安装 JRE 即可。

JDK、JRE 和 JVM 的包含关系

  1. JDK = JRE + 开发工具集(例如 Javac,java 编译工具等)

  2. JRE = JVM + Java SE 标准类库(java 核心类库)

  3. 如果只想运行开发好的 .class 文件 只需要 JRE

转义字符

\t :一个制表位,实现对齐的功能

\n :换行符

\\:一个\

\" :一个”

\' :一个’

\r :一个回车

注释

Java 中的注释类型

  1. 单行注释 //

  2. 多行注释 /* */

  3. 文档注释 /** */

  1. 被注释的文字,不会被 JVM(java 虚拟机)解释执行

  2. 多行注释里面不允许有多行注释嵌套

文档注释:

image-20220817211019132

生成程序的说明文档,

1
2
javadoc -d 输出目录 -author -version (要输出的信息) java文件
例如:javadoc -d D:\\@yjs2\\javacode\\doc -author -version javadoc.java

image-20220817212358760

image-20220817212420005

代码规范

两种风格都可以

image-20220817212919453

DOS 命令

image-20220817213139726

常用的 dos 命令

  1. 查看当前目录是有什么内容 dir

dir

dir d:\abc2\test200

  1. 切换到其他盘下:盘符号 cd : change directory

案例演示:切换到 c 盘

cd /D c:

  1. 切换到当前盘的其他目录下 (使用相对路径和绝对路径演示), ..\表示上一级目录

案例演示: cd d:\abc2\test200 cd ....\abc2\test200

  1. 切换到上一级:

案例演示: cd ..

  1. 切换到根目录:cd \

案例演示:cd \

  1. 查看指定的目录下所有的子级目录 tree

  2. 清屏 cls

  3. 退出 DOS exit

变量

image-20220817213841605

+号的使用

image-20220817214108672

数据类型

image-20220817214201652

整数类型

image-20220817214353489

整型的使用细节

image-20220817215607966

浮点类型

image-20220817215638568

  1. 关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位

  2. 尾数部分可能丢失,造成精度损失(小数都是近似值)。

浮点型使用细节

image-20220817215827404

字符类型(char)

字符类型可以表示单个字符,字符类型是 char,char 是两个字节(可以存放汉字),多个字符我们用字符串 String

image-20220817221340893

在 java 中,char 的本质是一个整数,在默认输出时,是 unicode 码对应的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//在 java 中,char 的本质是一个整数,在默认输出时,是 unicode 码对应的字符 
//要输出对应的数字,可以(int)字符
char c1 = 97;
System.out.println(c1); // a

char c2 = 'a'; //输出'a' 对应的 数字
System.out.println((int)c2); //97

//char 类型是可以进行运算的,相当于一个整数,因为它都对应有 Unicode 码.
System.out.println('a' + 10);//107

char c5 = 'b' + 1;//98+1==> 99
System.out.println((int)c5); //99
System.out.println(c5); //99->对应的字符->编码表 ASCII(规定好的)=>c

image-20220817221825359

布尔类型:boolean

image-20220817222057666

基本数据类型转换

自动类型转换

image-20220817222418011

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
//自动类型转换细节 
public class AutoConvertDetail {
//编写一个 main 方法
public static void main(String[] args) {
//细节 1: 有多种类型的数据混合运算时,
//系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
int n1 = 10; //ok
//float d1 = n1 + 1.1;//错误 n1 + 1.1 => 结果类型是 double
double d1 = n1 + 1.1;//对 n1 + 1.1 => 结果类型是 double
float d1 = n1 + 1.1F;//对 n1 + 1.1 => 结果类型是 float

//细节 2: 当我们把精度(容量)大 的数据类型赋值给精度(容量)小 的数据类型时,
//就会报错,反之就会进行自动类型转换。
//int n2 = 1.1;//错误 double -> int


//细节 3: (byte, short) 和 char 之间不会相互自动转换
//当把具体数赋给 byte 时,(1)先判断该数是否在 byte 范围内,如果是就可以
byte b1 = 10; //对 , -128-127
// int n2 = 1; //n2 是 int
// byte b2 = n2; //错误,原因: 如果是变量赋值,判断类型
// char c1 = b1; //错误, 原因 byte 不能自动转成 char

//细节 4: byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型
byte b2 = 1;
byte b3 = 2;
short s1 = 1;
//short s2 = b2 + s1;//错, b2 + s1 => int
int s2 = b2 + s1;//对, b2 + s1 => int
//byte b4 = b2 + b3; //错误: b2 + b3 => int

//boolean 不参与转换
boolean pass = true;
//int num100 = pass;// boolean 不参与类型的自动转换

//自动提升原则: 表达式结果的类型自动提升为 操作数中最大的类型
}

}
强制类型转换

自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符 ( ),但可能造成 精度降低或溢出,格外要注意。

image-20220817224848850

基本数据类型和 String 类型的转换

image-20220817225058700

运算符

1) 算术运算符

image-20220817225550484

image-20220817225701377

image-20220817225805811

image-20220817225951430

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
/**
* 演示算术运算符的使用
*/
public class ArithmeticOperator {
//编写一个 main 方法
public static void main(String[] args) {
System.out.println(10 / 4); //从数学来看是 2.5, java 中 2
System.out.println(10.0 / 4); //java 是 2.5
double d = 10 / 4;//java 中 10 / 4 = 2, 2=>2.0
System.out.println(d);// 是 2.0

// % 取模 ,取余
// 在 % 的本质 看一个公式!!!! a % b = a - a / b * b
// -10 % 3 => -10 - (-10)/3 * 3 = -10 + 9 = -1
// 10 % -3 => 10 - 10 / (-3) * -3 = 10 -9 = 1
// -10 % -3 => -10 - (-10) / (-3) * (-3) = -10 + 9 = -1
//
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1
System.out.println(10 % -3); //1
System.out.println(-10 % -3);//-1

//++的使用
//
int i = 10;
i++;//自增 等价于 i = i + 1; => i = 11
++i;//自增 等价于 i = i + 1; => i = 12

/*作为表达式使用
前++:++i 先自增后赋值
后++:i++先赋值后自增
*/
int j = 8;
//int k = ++j; //等价 j=j+1;k=j;
int k = j++; // 等价 k =j;j=j+1;
System.out.println("k=" + k + "j=" + j);//8 9
}

}

2) 赋值运算符

  1. 运算顺序从右往左

int num = a + b + c;

  1. 赋值运算符的左边 只能是变量,右边 可以是变量、表达式、常量值

int num = 20; int num2= 78 * 34 - 10; int num3 = a;

  1. 复合赋值运算符等价于下面的效果

比如:a+=3;等价于 a=a+3; 其他类推

  1. 复合赋值运算符会进行类型转换。

byte b = 2; b+=3; b++;

1
2
3
4
//复合赋值运算符会进行类型转换
byte b = 3;
b += 2; // 等价 b = (byte)(b + 2);
b++; // b = (byte)(b+1);

3) 关系运算符 [比较运算符]

  1. 关系运算符的结果都是 boolean 型,也就是要么是 true,要么是 false

  2. 关系表达式 经常用在 if 结构的条件中或循环结构的条件中

image-20220818142455842

4) 逻辑运算符

用于连接多个条件(多个关系表达式),最终的结果也是一个 boolean 值。

image-20220818142635520

逻辑运算规则:

  1. a&b : & 叫逻辑与:规则:当 a 和 b 同时为 true ,则结果为 true, 否则为 false

  2. a&&b : && 叫短路与:规则:当 a 和 b 同时为 true ,则结果为 true,否则为 false

  3. a|b : | 叫逻辑或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false

  4. a||b : || 叫短路或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false

  5. !a : 叫取反,或者非运算。当 a 为 true, 则结果为 false, 当 a 为 false 是,结果为 true

  6. a^b: 叫逻辑异或,当 a 和 b 不同时,则结果为 true, 否则为 false

&和&&的区别

  1. &&短路与:如果第一个条件为 false,则第二个条件不会判断,最终结果为 false,效率高

  2. & 逻辑与:不管第一个条件是否为 false,第二个条件都要判断,效率低

  3. 开发中, 我们使用的基本是使用短路与&&, 效率高

|| 和 | 使用区别

  1. ||短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为 true,效率高

  2. | 逻辑或:不管第一个条件是否为 true,第二个条件都要判断,效率低

  3. 开发中,我们基本使用 ||

5) 位运算符 [需要二进制基础]

点击进入二进制

java 中有 7 个位运算(&、|、^、~、>>、<<和 >>>)

image-20220818161403084

  1. 算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位

  2. 算术左移 <<: 符号位不变,低位补 0

  3. 逻辑右移也叫无符号右移,运算规则是: 低位溢出,高位补 0

  4. 特别说明:没有 <<< 符号

6) 三元运算符

条件表达式 ? 表达式 1: 表达式 2;

运算规则:

  1. 如果条件表达式为 true,运算后的结果是表达式 1;

  2. 如果条件表达式为 false,运算后的结果是表达式 2;

1
2
3
4
5
6
//表达式 1 和表达式 2 要为可以赋给接收变量的类型 
//(或可以自动转换/或者强制转换)
int a = 3;
int b = 8;
int c = a > b ? (int)1.1 : (int)3.4;//可以的
double d = a > b ? a : b + 3;//可以的,满足 int -> double

标识符的命名规则和规范

image-20220818145712565

键盘输入语句

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。Input.java , 需要一个 扫描器(对象), 就是 Scanner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
//Scanner 类 表示 简单文本扫描器,在 java.util 包
// 1. 引入/导入 Scanner 类所在的包
// 2. 创建 Scanner 对象 , new 创建一个对象,体会
Scanner myScanner = new Scanner(System.in);
//3. 接收用户输入了, 使用 相关的方法
System.out.println("请输入姓名");
//当程序执行到 next 方法时,会等待用户输入
String name = myScanner.next(); //接收用户输入字符串
System.out.println("请输入年龄");
Integer age = myScanner.nextInt(); //接收用户输入 int
System.out.println("请输入工资");
Double salary = myScanner.nextDouble(); //接收用户输入 double

System.out.println("姓名:" + name + ", 年龄:" + age + ", 工资:" + salary);
}

进制

对于整数,有四种表示方式:

二进制:0,1 ,满 2 进 1.以 0b 或 0B 开头。

十进制:0-9 ,满 10 进 1。

八进制:0-7 ,满 8 进 1. 以数字 0 开头表示。

十六进制:0-9 及 A(10)-F(15),满 16 进 1. 以 0x 0X 开头表示。此处的 A-F 不区分大小写。

进制的转换

二进制转换成十进制

image-20220818155356435

八进制转换成十进制

image-20220818155428220

十六进制转换成十进制

规则:从最低位(右边)开始,将每个位上的数提取出来,乘以 16 的(位数-1)次方,然后求和。

案例:请将 0x23A 转成十进制的数

0x23A = 10 * 16^0 + 3 * 16 ^ 1 + 2 * 16^2 = 10 + 48 + 512 = 570

十进制转换成二进制

规则:将该数不断除以 2,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的二进制。

案例:请将 34 转成二进制 = 0B00100010

image-20220818155712476

十进制转换成八进制

规则:将该数不断除以 8,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的八进制。

案例:请将 131 转成八进制 => 0203

image-20220818155750616

十进制转换成十六进制

规则:将该数不断除以 16,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的十六进制。

案例:请将 237 转成十六进制 => 0xED

image-20220818155827702

二进制转换成八进制

规则:从低位开始,将二进制数每三位一组,转成对应的八进制数即可。

案例:请将 ob11010101 转成八进制 ob11(3)010(2)101(5) => 0325

二进制转换成十六进制

规则:从低位开始,将二进制数每四位一组,转成对应的十六进制数即可。

案例:请将 ob11010101 转成十六进制 ob1101(D)0101(5) = 0xD5

八进制转换成二进制

规则:将八进制数每 1 位,转成对应的一个 3 位的二进制数即可。

案例:请将 0237 转成二进制 02(010)3(011)7(111) = 0b10011111

十六进制转换成二进制

规则:将十六进制数每 1 位,转成对应的 4 位的一个二进制数即可。

案例:请将 0x23B 转成二进制 0x2(0010)3(0011)B(1011) = 0b001000111011

原码、反码、补码

image-20220818161156008

数组

image-20220818190859745

  1. 数组是多个相同类型数据的组合,实现对这些数据的统一管理

  2. 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。

  3. 数组创建后,如果没有赋值,有默认值

int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null

  1. 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组

  2. 数组的下标是从 0 开始的

  3. 数组下标必须在指定范围内使用,否则报:下标越界异常

  4. 数组属引用类型,数组型数据是对象(object)

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
package com.ep;

import java.util.Scanner;

public class Array1 {
public static void main(String[] args) {
//1. 创建一个 double 数组,大小 5

//(1) 第一种动态分配方式 //
//double scores[] = new double[5];

//(2) 第 2 种动态分配方式, 先声明数组,再 new 分配空间
double scores[]; //声明数组, 这时 scores 是 null
scores = new double[5]; // 分配内存空间,可以存放数据

//2. 循环输入 // scores.length 表示数组的大小/长度
Scanner myScanner = new Scanner(System.in);
for( int i = 0; i < scores.length; i++) {
System.out.println("请输入第"+ (i+1) +"个元素的值");
scores[i] = myScanner.nextDouble();
}

//输出,遍历数组
System.out.println("==数组的元素/值的情况如下:===");
for( int i = 0; i < scores.length; i++) {
System.out.println("第"+ (i+1) +"个元素的值=" + scores[i]);
}

int[] arr = {1,2,3,4,5};
}
}

数组赋值机制

  1. 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。

int n1 = 2; int n2 = n1;

  1. 数组在默认情况下是引用传递,赋的值是地址。

看一个案例,并分析数组赋值的内存图(重点, 难点. )。

//代码 ArrayAssign.java

int[] arr1 = {1,2,3};

int[] arr2 = arr1;

image-20220818214223011

二维数组

1
2
3
int arr[][] = new int[2][3]; // 动态初始化
int[][] arr = new int[3][]; // 动态初始化
int[][] arr = {{1,1,1}, {8,8,9}, {100}}; //静态初始化

类和对象

区别:

  1. 类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.

  2. 对象是具体的,实际的,代表一个具体事物, 即 是实例.

  3. 类是对象的模板,对象是类的一个个体,对应一个实例

中级部分

IDEA常用快捷键

  1. 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d

  2. 复制当前行, 自己配置 ctrl + alt + 向下光标

  3. 补全代码 alt + /

  4. 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】

  5. 导入该行需要的类 先配置 auto import , 然后使用 alt+enter 即可

  6. 快速格式化代码 ctrl + alt + L

  7. 快速运行程序 自己定义 alt + R

  8. 生成构造器等 alt + insert [提高开发效率]

  9. 查看一个类的层级关系 ctrl + H [学习继承后,非常有用]

  10. 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 [学继承后,非常有用]

  11. 自动的分配变量名 , 通过 在后面假 .var [老师最喜欢的]

  12. 还有很多其它的快捷键…

自定义模板

File -> settings -> editor -> live templates ->

image-20220819185445863

常用的包

  1. java.lang.* //lang 包是基本包,默认引入,不需要再引入.

  2. java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner

  3. java.net.* //网络包,网络开发

  4. java.awt.* //是做 java 的界面开发,GUI

访问修饰符

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用 public 修饰,对外公开

  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开

  3. 默认级别:没有修饰符号,向同一个包的类公开.

  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开

image-20220819185845299

继承

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问

  2. 子类必须调用父类的构造器, 完成父类的初始化 (子类无参构造函数默认会调用super()方法,所以就调用了父类的无参构造方法)

  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]

  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)

  5. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

  6. super()this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

  7. java 所有类都是 Object 类的子类, Object 是所有类的基类.

  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

  9. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。

思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

  1. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

super 关键字

image-20220819202936175

super和this的比较

image-20220819203224547

重写

image-20220819203509640

重写和重载

image-20220819204455002

多态

多态的前提是:两个对象(类)存在继承关系

多态的向上转型

image-20220819205543603

多态向下转型

image-20220819205609922

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
package com.ep;

public class DuoTai {
public static void main(String[] args) {
//向上转型: 父类的引用指向了子类的对象
//语法:父类类型引用名 = new 子类类型();
Animal animal = new Cat();
Object object = new Cat(); // object也是Cat的父类
//向上转型调用方法的规则如下:
// (1)可以调用父类中的所有成员(需遵守访问权限)
// (2)但是不能调用子类的特有的成员
// (#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// animal.catchMouse();错误
// (4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法

//多态的向下转型
// (1)语法:子类类型 引用名 =(子类类型)父类引用;
Cat cat = (Cat)animal;
cat.eatMOuse();
}
}

class Animal {
String name = "动物";
int age = 10;

public void sleep() {
System.out.println("睡");
}
public void eat() {
System.out.println("吃");
}
}

class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}

public void eatMOuse() {
System.out.println("吃老鼠");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}

public void eatBone() {
System.out.println("吃骨头");
}
}

instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

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
package com.ep;

public class InstanceofTest {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println(zi instanceof Zi); // true
System.out.println(zi instanceof Fu); // true

// 向上转型
Fu fu = new Zi();
System.out.println(fu instanceof Zi); // true
System.out.println(fu instanceof Fu); // true

Object obj = new Object();
System.out.println(obj instanceof Fu); // false

String str = "hello";
System.out.println(str instanceof Object); // true
}
}

class Fu { // 父类

}

class Zi extends Fu { // 子类

}

动态绑定机制

image-20220819212809794

多态数组

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
package com.ep.polyArray;

public class PolyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100.0);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000.0);
persons[4] = new Teacher("king", 50, 25000.0);

//循环遍历多态数组,调用 say
for (int i = 0; i < persons.length; i++) {
persons[i].say();
if (persons[i] instanceof Student) {
Student student = (Student) persons[i];
student.study();
}else if (persons[i] instanceof Teacher){
Teacher teacher = (Teacher) persons[i];
teacher.teach();
}
}

}
}

多态参数

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
package com.ep.polyParameter;

public class PolyPrarmeter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 1);
Manager milan = new Manager("milan", 2, 10);
PolyPrarmeter polyPrarmeter = new PolyPrarmeter();
polyPrarmeter.showEmpAnnual(tom);
polyPrarmeter.showEmpAnnual(milan);
polyPrarmeter.testWork(tom);
polyPrarmeter.testWork(milan);
}

public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());//动态绑定机制.
}

public void testWork(Employee e){
if (e instanceof Worker){
((Worker) e).work();
}else if (e instanceof Manager) {
((Manager) e).manage();
}
}

}

Object类

alt + 7 打开Structure窗口

equals

==和 equals 的对比

image-20220819225351611

image-20220819225405853

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
78
79
80
81
package com.ep.object;

public class Equals {
public static void main(String[] args) {

A a = new A();
A b = a;
A c = a;

int num1 = 10;
int num2 = 10;

// == 判断引用类型判断的是引用地址是否相同
System.out.println(a == b); // true
System.out.println(c == a); // true

// 基本数据类型,判断值是否相等
System.out.println(num1 == num2);

/**
* 原生Object的equals方法
* public boolean equals(Object obj) {
* return (this == obj);
* }
*/

/**
* String 类的 equals 方法 把 Object 的 equals 方法重写了,变成了比较两个字符串值是否相同
* public boolean equals(Object anObject) {
* if (this == anObject) { // 如果是同一个对象
* return true;
* }
* if (anObject instanceof String) { // 是字符串
* String anotherString = (String)anObject; //向下转型
* int n = value.length;
* if (n == anotherString.value.length) { // 长度相同,判断值是否相同
* char v1[] = value;
* char v2[] = anotherString.value;
* int i = 0;
* while (n-- != 0) {
* if (v1[i] != v2[i])
* return false;
* i++;
* }
* return true;
* }
* }
* return false;
* }
*/
"hello".equals("abc");

/**
* Integer 也重写了 Object 的 equals 方法
* public boolean equals(Object obj) {
* if (obj instanceof Integer) {
* return value == ((Integer)obj).intValue();
* }
* return false;
* }
*/
new Integer(5).equals(5);
System.out.println(" ==============================");

Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
System.out.println(integer1 == integer2);//false 判断的是地址
System.out.println(integer1.equals(integer2));//true,判断的是值

String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true

}
}

class A{

}

重写equals方法
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
package com.ep.object;

public class OverrideEquals {
public static void main(String[] args) {
Person person = new Person("jack", 10, "男");
Person person2 = new Person("jack", 10, "男");
System.out.println(person == person2); // false
System.out.println(person.equals(person2)); // true
}
}


class Person {
private String name;
private Integer age;
private String sex;
// 重写 Objece的equals方法


@Override
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回 true
if ( this == obj) {
return true;
}
if (obj instanceof Person) { ////类型判断
//进行 向下转型, 因为我需要得到 obj 的 各个属性
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age && this.sex == p.sex;
}else {
return false;
}
}

public Person(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}
}

hashCode

image-20220820170901876

  1. 提高具有哈希结构的容器的效率!

  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的

  4. 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HashCodeTest {
public static void main(String[] args) {
AA aa1 = new AA();
AA aa2 = new AA();
AA aa3 = aa1;
System.out.println(aa1.hashCode());
System.out.println(aa2.hashCode());
System.out.println(aa3.hashCode()); // 和aa1指的是同一个对象,hash值相同

}
}
class AA{

}

toString

  1. 基本介绍

​ 默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】

​ 子类往往重写 toString 方法,用于返回对象的属性信息

  1. 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式.

  2. 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用 monster.toString()

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
package com.ep.object;

public class ToStringTest {
public static void main(String[] args) {
Stu stu = new Stu("张三", 18, 98);
System.out.println(stu.toString() + " hashCode = " + stu.hashCode());
System.out.println(stu.toString());
}
}

class Stu{
private String name;
private Integer age;
private double score;
/*
Object 的 toString() 源码
(1)getClass().getName() 类的全类名(包名+类名 )
(2)Integer.toHexString(hashCode()) 将对象的 hashCode 值转成 16 进制字符串
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/

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

public Stu(String name, Integer age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
}

finalize

  1. 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作.

  2. 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来 销毁该对象,在销毁该对象前,会先调用 finalize 方法。

  3. 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制.

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
package com.ep.object;

public class FinalizeTest {
public static void main(String[] args) {
Car car = new Car("宝马");
//这时 car 对象就是一个垃圾,垃圾回收器就会回收(销毁)对象, 在销毁对象前,会调用该对象的 finalize 方法
// ,程序员就可以在 finalize 中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
// ,如果程序员不重写 finalize,那么就会调用 Object 类的 finalize, 即默认处理
car = null;
System.gc(); //主动调用垃圾回收器
System.out.println("程序退出了");
}
}
class Car {
private String name;

@Override
protected void finalize() throws Throwable {
System.out.println(this.name + "被销毁");
System.out.println("资源被回收");
}

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

断点调试(debug)

image-20220820175218562

image-20220820175236439

快捷键

F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)

F7:跳入方法内

F8: 逐行执行代码.

shift+F8: 跳出方法

image-20220820175318196

image-20220820180219228

进入不到方法,强制进入 alt+shift+f7或者

image-20220820181556773

image-20220820181745435

断点可以在 debug 过程中,动态的下断点

image-20220820182420089

(面向对象)高级部分

类变量

image-20220820212500692

image-20220820212515072

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
package com.ep.class1;

public class ClassParameter {
public static void main(String[] args) {
Stu stu1 = new Stu("zhangsan", 15);
Stu stu2 = new Stu("lisi", 15);
Stu stu3 = new Stu("wangwu", 15);

System.out.println(stu1.count);
System.out.println(stu2.count);
System.out.println(Stu.count);
}
}

class Stu{

public static Integer count = 0;

private String name;
private Integer age;

public Stu(String name, Integer age) {
this.name = name;
this.age = age;
Stu.count ++;
}
}

类方法

image-20220820213432934

image-20220820213553744

image-20220820214421313

image-20220820214436240

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
package com.ep.class1;

public class staticTest {
public static int n1;
public int n2;

/**
* 普通方法
*/
public void test() {
hello();
System.out.println(n1);
}

/***
* 静态方法
*/
public static void hello() {
// this.n2 错误
//类方法中不允许使用和对象有关的关键字,
// 比如 this 和 super。普通方法(成员方法)可以。
System.out.println(n1);
// test();错误
}
}


main 方法

image-20220820215414726

image-20220820215902000

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
package com.ep.Main;

public class Main01 {

// 静态的变量/属性
public static String name = "静态变量,都可以访问";

//非静态的变量/属性
public int n1 = 1;

// 静态方法
public static void hi() {}

// 非静态方法
public void hello(){}

public static void main(String[] args) {
//可以直接使用 name
// 1. 静态方法 main 可以访问本类的静态成员
System.out.println(name);
//2. 静态方法 main 不可以访问本类的非静态成员
// System.out.println(n1); 错误,不可以使用
// hello(); // 也不可以使用非静态方法

hi();

//若想使用非晶态变量和方法需要创建对象
System.out.println(new Main01().n1);
new Main01().hello();

}
}

代码块

image-20220820222002440

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
public class CodeBlock1 {
public static void main(String[] args) {
// 代码块里的内容会自动执行
Movie movie = new Movie("你好李焕英");

new Movie("大闹天宫",19.9,"lisi");
}

}

class Movie{
private String name;
private double price;
private String author;

//(1) 下面的三个构造器都有相同的语句
// (2) 这样代码看起来比较冗余
// (3) 这时我们可以把相同的语句,放入到一个代码块中,即可
// (4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
// (5) 代码块调用的顺序优先于构造器
{
// 代码块里面的内容会优先于构造器执行
System.out.println("电影屏幕打开.....");
System.out.println("电影广告开始播放.....");
System.out.println("电影开始播放.....");
}

public Movie(String name) {
this.name = name;
System.out.println(name + "开始播放...");
}

public Movie(String name, double price) {
this.name = name;
this.price = price;
System.out.println(name + "开始播放...");
}

public Movie(String name, double price, String author) {
this.name = name;
this.price = price;
this.author = author;
System.out.println(name + "开始播放...");
}
}

image-20220820223823431

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
package com.ep.codeBlock;

public class CodeBlock2 {
public static void main(String[] args) {
//类被加载的情况举例
// 1. 创建对象实例时(new)
// AA aa = new AA();
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载

//3. 使用类的静态成员时(静态属性,静态方法)
System.out.println(AA.n1);

// DD dd = new DD();
// DD dd1 = new DD();

//static 代码块,是在类加载时,执行的,而且只会执行一次.
//普通的代码块,在创建对象实例时,会被隐式的调用。
// 被创建一次,就会调用一次。
// 如果只是使用类的静态成员时,普通代码块并不会执行
System.out.println(DD.m);
}
}

class DD {
public static int m = 3;
static {
System.out.println("DD的静态代码块,只会执行一次");
}
{
System.out.println("DD的非静态代码块,创建实例就会执行");
}
}

class AA extends BB {
public static int n1 = 1;
static {
System.out.println("AA的静态代码块执行了");
}
}
class BB {
static {
System.out.println("父类的静态代码块被执行了..");
}
}

[静态代码块,静态属性](按顺序) > [代码块,普通变量] (按顺序) > 构造器

image-20220820225620062

image-20220820230229067

image-20220821154449026

image-20220821154504695

单例设计模式

image-20220821160956456

image-20220821161021005

饿汉式:

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.ep.single_;

public class Single1 {
public static void main(String[] args) {
GrildFriend instance = GrildFriend.getInstance();
GrildFriend instance2 = GrildFriend.getInstance();
System.out.println(instance);
System.out.println(instance == instance2); // true
}
}

class GrildFriend {

private String name;

private GrildFriend(String name) {
this.name = name;
}

//为了能够在静态方法中,返回 gf 对象,需要将其修饰为 static
// 对象,通常是重量级的对象, 饿汉式可能造成创建了对象,但是沒有使用.
private static GrildFriend gf = new GrildFriend("小红");

//如何保障我们只能创建一个 GirlFriend 对象
// 步骤[单例模式-饿汉式]
// 1. 将构造器私有化
// 2. 在类的内部直接创建对象(该对象是 static)
// 3. 提供一个公共的 static 方法,返回 gf 对象
public static GrildFriend getInstance() {
return gf;
}

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

懒汉式:

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
public class single2 {
public static void main(String[] args) {
Cat instance = Cat.getInstance();
Cat instance2 = Cat.getInstance();
System.out.println(instance == instance2); // true
}
}

class Cat{
private String name;

private static Cat cat; // 默认是 null

//步驟
// 1.仍然构造器私有化
// 2.定义一个static 静态对象
// 3.提供一个public 的 static 方法,可以返回一個个Cat对象
// 4.懒汉式,只有当用户使用 getInstance 时,才返回 cat 对象, 后面再次调用,会返回上次创建的对象
// 从而保证了单例
private Cat(String name) {
this.name = name;
}

public static Cat getInstance(){
if (cat == null) { // 会存在线程安全问题
cat = new Cat("小猫");
}
return cat;
}
}

final

image-20220821165759690

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
package com.ep.final_;


public class final01 {
public static void main(String[] args) {
//E.MAX = 10000;
}
}

//如果我们要求 A 类不能被其他类继承
// 可以使用 final 修饰 A 类
final class A {
}

//class B extends A { } // 错误,A类被final关键词修饰,不可以被继承

class C {
//如果我们要求 hi 不能被子类重写
// 可以使用 final 修饰 hi 方法
public final void hi() {
}
}

class D extends C {
// @Override
// public void hi() { // 加了final关键字,就不可以被修改了
// System.out.println("重写了 C 类的 hi 方法..");
// }
}

//当不希望类的的某个属性的值被修改,可以用 final 修饰
class E{
public final static int MAX = 1000; // 值不可以被修改
}

//当不希望某个局部变量被修改,可以使用 final 修饰
class F {
public void cry() {
//这时,NUM 也称为 局部常量
final double NUM = 0.01;
//NUM = 0.9; // 不可以被修改
System.out.println("NUM=" + NUM); }
}

image-20220821171718450

image-20220821172104511

比如将一个变量,定义为 public final static int n1 = 100, 那么在使用n1的时候不会导致类加载,也就不会导致类的静态代码块执行

image-20220821172234016

抽象类

//===> 所谓抽象方法就是没有实现的方法

//===> 所谓没有实现就是指,没有方法体

//===> 当一个类中存在抽象方法时,需要将该类声明为 abstract 类

//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.

image-20220821173453909

image-20220821173516050

image-20220821173539355

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 class Abstract01 {
public static void main(String[] args) {
//抽象类,不能被实例化
//new A();
}
}

//抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法
//,还可以有实现的方法。
abstract class A {
public void hi() {
System.out.println("hi");
}
}

//一旦类包含了 abstract 方法,则这个类必须声明为 abstract
abstract class B {
public abstract void hi();
}

//abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
// public abstract int n1 = 1;
}

image-20220821173740054

image-20220821173853750

模板设计模式

image-20220821181908417

image-20220821181920907

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Template { //抽象类-模板设计模式

public abstract void job(String name); //抽象方法

public void calculateTime(String name) {
long start = System.currentTimeMillis();
job(name);
long end = System.currentTimeMillis();
System.out.println(name+"工作时间:"+ (end - start));
}
}

接口

image-20220821183257900

image-20220821183410916

//1.接口不能被实例化

//2.接口中所有的方法是 public 方法, 接口中抽象方法,可以不用 abstract 修饰

//3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用 alt+enter 来解决

//4.抽象类去实现接口时,可以不实现接口的抽象方法

image-20220821183619624

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
public class Interface01 {
public static void main(String[] args) {
// 接口中的属性,是 public static final
System.out.println(IB.n1);//说明 n1 就是 static
//IB.n1 = 30; 说明 n1 是 final
}
}

interface IB {
//接口中的属性,只能是 final 的,而且是 public static final 修饰符
int n1 = 10; //等价 public static final int n1 = 10;
void hi();
}

interface IC {
void say();
}

//接口不能继承其它的类,但是可以继承多个别的接口

interface ID extends IB, IC {}

//接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE {}

//一个类同时可以实现多个接口
class Pig implements IB,IC {
public void hi() {}
public void say() {}
}

小结: 当子类继承了父类,就自动的拥有父类的功能

如果子类需要扩展功能,可以通过实现接口的方式扩展.

可以理解 实现接口 是 对 java 单继承机制的一种补充

image-20220821190439827

接口多态

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
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态体现
//接口类型的变量 if01 可以指向 实现了 IF 接口类的对象实例
IF if01 = new Monster();
if01 = new Car();

//继承体现的多态
//父类类型的变量 a 可以指向 继承 AAA 的子类的对象实例
AAA a = new BBB();
a = new CCC();
}
}

interface IF {}

class Monster implements IF { }

class Car implements IF { }

class AAA { }

class BBB extends AAA { }

class CCC extends AAA { }
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
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组 -> 接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
/*
给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
还需要调用 Phone 特有方法 call
*/
for (int i = 0; i < usbs.length; i++) {
usbs[i].work();//动态绑定..
//和前面一样,我们仍然需要进行类型的向下转型
if (usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
((Phone_) usbs[i]).call();
}
}
}
}

interface Usb {
void work();
}

class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
public void work() {
System.out.println("手机工作中...");
}
}

class Camera_ implements Usb {
public void work() {
System.out.println("相机工作中...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 演示多态传递现象
*/
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果 IG 继承了 IH 接口,而 Teacher 类实现了 IG 接口
//那么,实际上就相当于 Teacher 类也实现了 IH 接口.
//这就是所谓的 接口多态传递现象.
IH ih = new Teacher();
}
}

interface IH {
void hi();
}

interface IG extends IH{ }

class Teacher implements IG {
public void hi() {}
}

对于一个类同时继承父类和实现接口,想要访问x

//访问接口A的 x 就使用 A.x

//访问父类的 x 就使用 super.x

内部类

如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2) 匿名内部类

定义在成员位置 (1) 成员内部类 (2) 静态内部类

image-20220821203832589

image-20220821203850077

内部类的分类

image-20220821204007784

局部内部类

image-20220821204106867

image-20220821204137095

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
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {//
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
System.out.println("outer02 的 hashcode=" + outer02);
}
}

class Outer02 {//外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}//私有方法

public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用 final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
//使用 外部类名.this.成员)去访问
//Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象
System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}
//6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
匿名内部类

image-20220821205704130

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}

class Outer04 { //外部类
private int n1 = 10;//属性
public void method() {//方法
//基于接口的匿名内部类
//1.需求: 想使用 IA 接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.现在需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger 的编译类型 ? IA
//6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1

/*
我们看底层 会分配 类名 Outer04$1
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7. jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址返回给 tiger
//8. 匿名内部类使用一次,就不能再使用
IA tiger = new IA() {
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger 的运行类型=" + tiger.getClass());
tiger.cry();
tiger.cry();
tiger.cry();

//IA tiger = new Tiger();


//演示基于类的匿名内部类
//分析
//1. father 编译类型 Father
//2. father 运行类型 Outer04$2
//3. 底层会创建匿名内部类
/*
class Outer04$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了 test 方法");
}
}
*/

//4. 同时也直接返回了 匿名内部类 Outer04$2 的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了 test 方法");
}
};
System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2
father.test();


//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
animal.eat();
}
}

interface IA {//接口
public void cry();
}


class Father {//类
public Father(String name) {//构造器
System.out.println("接收到 name=" + name);
}
public void test() {//方法
}
}

abstract class Animal { //抽象类
abstract void eat();
}

image-20220821213428923

image-20220821213441079

image-20220821213454099

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
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
//外部其他类---不能访问----->匿名内部类
System.out.println("main outer05 hashcode=" + outer05);
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person(){
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi 方法 n1=" + n1 + " 外部内的 n1=" + Outer05.this.n1 );
//Outer05.this 就是调用 f1 的 对象
System.out.println("Outer05.this hashcode=" + Outer05.this);
}
};
p.hi();//动态绑定, 运行类型是 Outer05$1
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}


new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了 hi 方法,哈哈...");
}
@Override
public void ok(String str) {
super.ok(str);
}
}.ok("jack");
}
}

class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
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 class InnerClassExercise {
public static void main(String[] args) {
/*
1.有一个铃声接口 Bell,里面有个 ring 方法。(右图)
2.有一个手机类 Cellphone,具有闹钟功能 alarmClock,参数是 Bell 类型(右图)
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
*/
CellPhone cellPhone = new CellPhone();

//1. 传递的是实现了 Bell 接口的匿名内部类 InnerClassExercise02$1
//2. 重写了 ring
//3. Bell bell = new Bell() {
// @Override
// public void ring() {
// System.out.println("懒猪起床了");
// }
//

cellPhone.alarmClock(new Bell() { //
public void ring() {
System.out.println("懒猪起床了");
}
});
cellPhone.alarmClock(new Bell() {
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}

interface Bell{ //接口
void ring();//方法
}

class CellPhone{//类
public void alarmClock(Bell bell){//形参是 Bell 接口类型
System.out.println(bell.getClass());
bell.ring();//动态绑定
}
}
成员内部类

image-20220821215249970

image-20220821215301933

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
public class MemberInnerClass {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
// 第一种方式
// outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
// 这就是一个语法,不要特别的纠结.
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();

// 第三种方式:本质也是第一种方式
new Outer08().new Inner08();
}
}

class Outer08 { //外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
public class Inner08 {//成员内部类
private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
//,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1);
hi();
}
}
//方法,返回一个 Inner08 实例
public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法
public void t1() {

//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}
静态内部类

image-20220821221604810

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
public class StaticInnerClass {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式 1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式 2
//编写一个方法,可以返回静态内部类的对象实例.
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}

class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10 就是静态内部类
//1. 放在外部类的成员位置
//2. 使用 static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner10 {
private static String name = "张三";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类 name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}

public static Inner10 getInner10_() {
return new Inner10();
}
}

枚举和注解

枚举

自定义类实现枚举

image-20220821224001249

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
public class Enumeration {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
}
//演示自定义枚举实现
class Season {//类
private String name;
private String desc;//描述
//定义了四个对象, 固定.
public static final Season SPRING = new Season("春天", "温暖");
public static final Season WINTER = new Season("冬天", "寒冷");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season SUMMER = new Season("夏天", "炎热");

//1. 将构造器私有化,目的防止 直接 new
//2. 去掉 setXxx 方法, 防止属性被修改
//3. 在 Season 内部,直接创建固定的对象
//4. 优化,可以加入 final 修饰符
private Season(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 + '\'' +
'}';
}
}

enum实现

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
public class Enumercation2 {
public static void main(String[] args) {
System.out.println(Season2.SPRING);
}
}


enum Season2 {
//定义了四个对象, 固定.
// public static final Season SPRING = new Season("春天", "温暖");
// public static final Season WINTER = new Season("冬天", "寒冷");
// public static final Season AUTUMN = new Season("秋天", "凉爽");
// public static final Season SUMMER = new Season("夏天", "炎热");

//如果使用了 enum 来实现枚举类
//1. 使用关键字 enum 替代 class
//2. public static final Season SPRING = new Season("春天", "温暖") 直接使用
//SPRING("春天", "温暖") 解读 常量名(实参列表)
//3. 如果有多个常量(对象), 使用 ,号间隔即可
//4. 如果使用 enum 来实现枚举,要求将定义常量对象,写在前面
//5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()

// 一定要放在最上面
SPRING("春天","凉爽"), // 对应有参构造函数
WINTER("冬天", "寒冷"),
AUTUMN("秋天", "凉爽"),
SUMMER("夏天", "炎热"),
WHAT // 对应无参构造函数,可以不加()
;
private String name;
private String Desc;
private Season2(String name, String desc) {
this.name = name;
Desc = desc;
}
private Season2(){}
}

注意事项

  1. 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类[如何证明],使用 javap 工 具来演示

  2. 传统的 public static final Season2 SPRING = new Season2(“春天”, “温暖”); 简化成 SPRING(“春天”, “温暖”), 这里必 须知道,它调用的是哪个构造器.

  3. 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略

  4. 当有多个枚举对象时,使用,间隔,最后有一个分号结尾

  5. 枚举对象必须放在枚举类的行首

image-20220822171208085

image-20220822172005609

image-20220822172217934

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Enumeration3 {
public static void main(String[] args) {
Gender boy = Gender.BOY;
Gender boy2 = Gender.BOY;
System.out.println(boy); // 输出的是BOY,
// 本质是调用Gender的父类Enum的toString方法
// public String toString() {
// return name;
// }

System.out.println(boy == boy2); // TRUE
}
}

enum Gender{
BOY, GIRL; //这里其实就是调用 Gender 类的无参构造器
}

enum常用方法

使用关键字 enum 时,会隐式继承 Enum 类, 这样我们就可以使用 Enum 类相关的方法

1
2
3
public abstract class Enum<E extends Enum<E>> 
implements Comparable<E>, Serializable {
}

image-20220822181632720

  1. toString:Enum 类已经重写过了,返回的是当前对象 名,子类可以重写该方法,用于返回对象的属性信息

  2. name:返回当前对象名(常量名),子类中不能重写

  3. ordinal:返回当前对象的位置号,默认从 0 开始

  4. values:返回当前枚举类中所有的常量

  5. valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常!

  6. compareTo:比较两个枚举常量,比较的就是编号!

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
public class EnumMethod {
public static void main(String[] args) {
//使用 Season2 枚举类,来演示各种方法
Season2 autumn = Season2.AUTUMN;
//输出枚举对象的名字
System.out.println(autumn.name()); // AUTUMN
//ordinal() 输出的是该枚举对象的次序/编号,从 0 开始编号
//AUTUMN 枚举对象是第三个,因此输出 2
System.out.println(autumn.ordinal());
//从反编译可以看出 values 方法,返回 Season2[]
//含有定义的所有枚举对象
for (Season2 value : Season2.values()) {//增强 for 循环
System.out.println(value); // SPRING WINTE AUTUMN SUMMER WHAT
}
//执行流程
// 1. 根据你输入的 "AUTUMN" 到 Season2 的枚举对象去查找
// 2. 如果找到了,就返回,如果没有找到,就报错
Season2 autumn1 = Season2.valueOf("AUTUMN");
System.out.println(autumn1); // AUTUMN

//compareTo:比较两个枚举常量,比较的就是编号,返回的是两个编号的差值,0代表相等
System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));
}
}

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
public class EnumExercise {
public static void main(String[] args) {
for (Week value : Week.values()) {
System.out.println(value);
}

}
}

/*声明 Week 枚举类,其中包含星期一至星期日的定义;
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
使用 values 返回所有的枚举数组, 并遍历 , 输出左图效果 */

enum Week {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期天");

private String name;

private Week(String name) {
this.name = name;
}

@Override
public String toString() {
return name;
}
}
  1. 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制。

  2. 枚举类和普通类一样,可以实现接口,如下形式。

enum 类名 implements 接口 1,接口 2{}

注解

  1. 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。

  2. 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。

  3. 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等。

使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元 素

三个基本的 Annotation:

  1. @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法

  2. @Deprecated: 用于表示某个程序元素(类, 方法等)已过时

  3. @SuppressWarnings: 抑制编译器警告

@Override

image-20220822185021980

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
class Father{//父类
public void fly(){
System.out.println("Father fly...");
}
public void say(){}
}

class Son extends Father {//子类
//1. @Override 注解放在 fly 方法上,表示子类的 fly 方法时重写了父类的 fly
//2. 这里如果没有写 @Override 还是重写了父类 fly
//3. 如果你写了@Override 注解,编译器就会去检查该方法是否真的重写了父类的
//方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
//4. 看看 @Override 的定义
//解读: 如果发现 @interface 表示一个 注解类
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
*/
@Override
public void fly() {
System.out.println("Son fly....");
}
@Override
public void say() {}

}

image-20220822185217317

@Deprecated

@Deprecated: 用于表示某个程序元素(类, 方法等)已过时

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
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
a.hi();
System.out.println(a.n1);
}
}
//1. @Deprecated 修饰某个元素, 表示该元素已经过时
//2. 即不在推荐使用,但是仍然可以使用
//3. 查看 @Deprecated 注解类的源码
//4. 可以修饰方法,类,字段, 包, 参数 等等
//5. @Deprecated 可以做版本升级过渡使用
/*
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
*/
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi(){}
}

image-20220822185429821

@SuppressWarnings

@SuppressWarnings: 抑制编译器警告

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
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
public class SuppressWarnings_ {
//1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings 注解来抑制警告信息
//2. 在{""} 中,可以写入你希望抑制(不显示)警告信息
//3. 可以指定的警告类型有
//all,抑制所有警告
//boxing,抑制与封装/拆装作业相关的警告
//cast,抑制与强制转型作业相关的警告
//dep-ann,抑制与淘汰注释相关的警告
//deprecation,抑制与淘汰的相关警告
//fallthrough,抑制与 switch 陈述式中遗漏 break 相关的警告
//finally,抑制与未传回 finally 区块相关的警告
//hiding,抑制与隐藏变数的区域变数相关的警告
//incomplete-switch,抑制与 switch 陈述式(enum case)中遗漏项目相关的警告
//javadoc,抑制与 javadoc 相关的警告
//nls,抑制与非 nls 字串文字相关的警告
//null,抑制与空值分析相关的警告
//rawtypes,抑制与使用 raw 类型相关的警告
//resource,抑制与使用 Closeable 类型的资源相关的警告
//restriction,抑制与使用不建议或禁止参照相关的警告
//serial,抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告
//static-access,抑制与静态存取不正确相关的警告
//static-method,抑制与可能宣告为 static 的方法相关的警告
//super,抑制与置换方法相关但不含 super 呼叫的警告
//synthetic-access,抑制与内部类别的存取未最佳化相关的警告
//sync-override,抑制因为置换同步方法而遗漏同步化的警告
//unchecked,抑制与未检查的作业相关的警告
//unqualified-field-access,抑制与栏位存取不合格相关的警告
//unused,抑制与未用的程式码及停用的程式码相关的警告
//4. 关于 SuppressWarnings 作用范围是和你放置的位置相关
//比如 @SuppressWarnings 放置在 main 方法,那么抑制警告的范围就是 main
// 通常我们可以放置具体的语句, 方法, 类.
//5. 看看 @SuppressWarnings 源码
//(1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
//(2) 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}
/**
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
*/

public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
int i;
System.out.println(list.get(1));
}
public void f1() {
@SuppressWarnings({"rawtypes"})
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
@SuppressWarnings({"unused"})
int i;
System.out.println(list.get(1));
}
}

image-20220822185941862

元注解

JDK 的元 Annotation 用于修饰其他 Annotation

  1. Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME

  2. Target // 指定注解可以在哪些地方使用

  3. Documented //指定该注解是否会在 javadoc 体现

  4. Inherited //子类会继承父类注解

image-20220822191350514

image-20220822191414785

image-20220822191428447

image-20220822191440819

异常-Exception

image-20220822203303354

image-20220822203317566

运行时异常

  1. NullPointerException 空指针异常

  2. ArithmeticException 数学运算异常

  3. ArrayIndexOutOfBoundsException 数组下标越界异常

  4. ClassCastException 类型转换异常

  5. NumberFormatException 数字格式不正确异常[]

编译时异常

异常处理

image-20220822205831146

try-catch-finally

快捷键:选中代码,ctrl+alt+t,然后选择6.try-catch

image-20220822205900770

image-20220822205954811

image-20220822210646153

image-20220822210705939

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
public class tryCatch {
public static void main(String[] args) {
//1.如果 try 代码块有可能有多个异常
//2.可以使用多个 catch 分别捕获不同的异常,相应处理
//3.要求子类异常写在前面,父类异常写在后面
try {
Person person = new Person();
person = null;
System.out.println(person.getName());//NullPointerException
int n1 = 10;
int n2 = 0;
int res = n1 / n2;//ArithmeticException
System.out.println("前面有异常,我就不会输出");
} catch (NullPointerException e) { // 子类异常要写在前面
System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());

} finally {
System.out.println("不管有没有异常,我都会执行");
}
}
}

class Person {
private String name = "jack";
public String getName() {
return name;
}
}

image-20220822210944884

1
2
3
4
5
6
7
8
9
10
11
public class tryFinally {
public static void main(String[] args) {
try {
int res = 1/0;
System.out.println("发生异常,我不会执行");
} finally {
System.out.println("这段代码要执行");
}
System.out.println("try-finally,如果有异常,这里不会执行,直接结束程序");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TryCatchExcrise {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr = "";
while (true) {
System.out.println("请输入一个数字");
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr);
break;
} catch (NumberFormatException e) {
System.out.println("输入有误,请重新输入");
}
}
System.out.println("你输入的是:" + num);

}
}

面试题

image-20220822222804142

image-20220822222924013

image-20220822223048666

throws

image-20220822205929605

image-20220822220825001

image-20220822220906768

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
public class ThrowsDetail {
public static void main(String[] args) {
f2();
}

public static void f2() /*throws ArithmeticException*/ {
//1.对于编译异常,程序中必须处理,比如 try-catch 或者 throws
//2.对于运行时异常,程序中如果没有处理,默认就是 throws 的方式处理
int n1 = 10;
int n2 = 0;
double res = n1 / n2;
}

public static void f1() throws FileNotFoundException {
//这里大家思考问题 调用 f3() 报错
//1. 因为 f3() 方法抛出的是一个编译异常
//2. 即这时,就要 f1() 必须处理这个编译异常
//3. 在 f1() 中,要么 try-catch-finally ,或者继续 throws 这个编译异常
f3(); // 抛出异常
}

public static void f3() throws FileNotFoundException { //编译异常
FileInputStream fis = new FileInputStream("d://aa.txt");
}

public static void f4() {
//1. 在 f4()中调用方法 f5() 是 OK
//2. 原因是 f5() 抛出的是运行异常
//3. 而 java 中,并不要求程序员显示处理,因为有默认处理机制
f5();
}
public static void f5() throws ArithmeticException {
}
}

class Father { //父类
public void method() throws RuntimeException {
}
}

class Son extends Father {//子类
//3. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,
//所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型
//4. 在 throws 过程中,如果有方法 try-catch , 就相当于处理异常,就可以不必 throws
@Override
public void method() throws ArithmeticException { // 异常和父类一样或者是父类异常的子类
}
}

自定义异常

image-20220822221756212

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyException {

public static void main(String[] args) {
int age = 250;
if (age > 0 && age <= 150) {
System.out.println("年龄合法");
}else {
// 抛出一个自定义异常,这个是运行时异常,
// 如果是编译异常,main方法也需要抛出异常,因为运行时异常有默认处理机制
throw new AgeException("年龄不合法");
}
}
}

//1. 一般情况下,我们自定义异常是继承 RuntimeException
// 2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制
// 如果是编译异常,那么调用方法的类也要抛出异常,比较麻烦
class AgeException extends RuntimeException {
public AgeException(String message) { // 构造器
super(message);
}
}

throw 和 throws

image-20220822222515633

image-20220822223238373

常用类

包装类

image-20220822225142882

image-20220822225213457

image-20220822225231142

包装类和基本数据的转换

image-20220822225254954

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Interge01 {
public static void main(String[] args) {
//演示 int <--> Integer 的装箱和拆箱
// jdk5 前是手动装箱和拆箱

// 手动装箱 int->Integer
int n1 = 5;
Integer integer = new Integer(n1);
// 或者
Integer integer1 = Integer.valueOf(n1);

// 手动拆箱 Integer -> int
n1 = integer.intValue();


//jdk5 后,就可以自动装箱和自动拆箱
int n2 = 100;
Integer integer2 = n2; //自动装箱 底层其实调用了 Interge.valueOf(n2)方法

n2 = integer2; // 自动拆箱,底层其实调用了 integer2.intValue()方法
}
}

image-20220822225955266

包装类型和 String 类型的相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer)->String
Integer n = 1; // 自动装箱
// 方式1
String i = n + "";
// 方式二
String i2 = n.toString();
// 方式三
String i3 = String.valueOf(n);

//String -> 包装类(Integer)
String s = "123";
Integer n1 = Integer.parseInt(s); ;//使用到自动装箱
Integer n2 = new Integer(s);
}
}

Integer 类和 Character 类的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class WrapperMethod { 
public static void main(String[] args) {
System.out.println(Integer.MIN_VALUE); //返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值

System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));//判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写
}
}

image-20220823173347730

源码解读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1. 如果 i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
// 2. 如果不在 -128~127,就直接 new Integer(i)
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

Integer m = 1; //底层 Integer.valueOf(1); -> 阅读源码
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n); //T
//所以,这里主要是看范围 -128 ~ 127 就是直接返回
//,否则,就 new Integer(xx);
Integer x = 128;//底层 Integer.valueOf(1);
Integer y = 128;//底层 Integer.valueOf(1);
System.out.println(x == y);//False

image-20220823173938418

String 类

String 类的理解和创建对象

image-20220823185842925

image-20220823185934865

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
public class String01 {
public static void main(String[] args) {
//1.String 对象用于保存字符串,也就是一组字符序列
// 2. "jack" 字符串常量, 双引号括起的字符序列
// 3. 字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节
// 4. String 类有很多构造器,构造器的重载
// 常用的有 String s1 = new String();
// String s2 = new String(String original);
// String s3 = new String(char[] a);
// String s4 = new String(char[] a,int startIndex,int count)
// String s5 = new String(byte[] b)

//5. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
// 接口 Comparable [String 对象可以比较大小]
// 6. String 是 final 类,不能被其他的类继承
// 7. String 有属性 private final char value[]; 用于存放字符串内容
// 8. 一定要注意:value 是一个 final 类型, 不可以修改(需要功力):即 value 不能指向
// 新的地址,但是单个字符内容是可以变化

String name ="jack";
name = "tom";
final char values[] = {'a','b'};
values[0] = 'c';
char[] v2 = {'t','o','m'};
// values = v2; 这样是不允许的,说明地址不可以被修改
}
}

创建 String 对象的两种方式

1
2
String name = "abc" // 方式一 直接赋值
String name = new String("abc") // 方式二调用构造器

image-20220823193223804

image-20220823193305707

测试题

image-20220823193353592

image-20220823193502318

image-20220823193554504

image-20220823193641495

image-20220823193749968

字符串的特性

image-20220823195823714

image-20220823195938879

image-20220823200010300

image-20220823213904755

image-20220823214108389

image-20220823214151383

image-20220823214201384

String 类的常见方法

image-20220823220404695

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
public class StringMethod {
public static void main(String[] args) {
//1. equals 前面已经讲过了. 比较内容是否相同,区分大小写
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));// false

// 2.equalsIgnoreCase 忽略大小写的判断内容是否相等
String username = "johN";
if ("john".equalsIgnoreCase(username)) {
System.out.println("Success!");
} else {
System.out.println("Failure!");
}

// 3.length 获取字符的个数,字符串的长度
System.out.println("abc".length());

// 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从 0 开始,如果找不到,返回-1
String s1 = "wer@terwe@g";
int index = s1.indexOf('@');
System.out.println(index);// 3
System.out.println("weIndex=" + s1.indexOf("we"));//0

// 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从 0 开始,如果找不到,返回-1
s1 = "wer@terwe@g@";
index = s1.lastIndexOf('@');
System.out.println(index);//11
System.out.println("ter 的位置=" + s1.lastIndexOf("ter"));//4

// 6.substring 截取指定范围的子串
String name = "hello,张三";
//下面 name.substring(6) 从索引 6 开始截取后面所有的内容
System.out.println(name.substring(6));//截取后面的字符
//name.substring(0,5)表示从索引 0 开始截取,截取到索引 5-1=4 位置
System.out.println(name.substring(2,5));//llo
}
}

image-20220823220804484

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
78
public class StringMethod2 {
public static void main(String[] args) {
// 1.toUpperCase 转换成大写
String s = "heLLo";
System.out.println(s.toUpperCase());//HELLO

// 2.toLowerCase
System.out.println(s.toLowerCase());//hello

// 3.concat 拼接字符串
String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
System.out.println(s1);//宝玉林黛玉薛宝钗 together

// 4.replace 替换字符串中的字符
s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
//在 s1 中,将 所有的 林黛玉 替换成薛宝钗
// s1.replace() 方法执行后,返回的结果才是替换过的.
// 注意对 s1 没有任何影响
String s11 = s1.replace("宝玉", "jack");
System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉
System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉

// 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
// 1. 以 , 为标准对 poem 进行分割 , 返回一个数组
// 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \
String[] split = poem.split(",");
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
System.out.println("==分割后内容===");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}

// 6.toCharArray 转换成字符数组
s = "happy";
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.println(chs[i]);
}

// 7.compareTo 比较两个字符串的大小,如果前者大,
// 则返回正数,后者大,则返回负数,如果相等,返回 0
// (1) 如果长度相同,并且每个字符也相同,就返回 0
// (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小
//就返回 if (c1 != c2) {
// return c1 - c2;
// }
// (3) 如果前面的部分都相同,就返回 str1.len - str2.len
String a = "jcck";// len = 3
String b = "jack";// len = 4
System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2 的值

// 8.format 格式字符串
/* 占位符有:
%s 字符串 %c 字符 %d 整型 %.2f 浮点型
*/
String name = "john";
int age = 10;
double score = 56.857;
char gender = '男';
//将所有的信息都拼接在一个字符串.
String info =
"我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我! ";
System.out.println(info);
//1. %s , %d , %.2f %c 称为占位符
//2. 这些占位符由后面变量来替换
//3. %s 表示后面由 字符串来替换
//4. %d 是整数来替换
//5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理
//6. %c 使用 char 类型来替换
String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
}
}

StringBuffer类

image-20220823222526118

1
2
3
4
5
6
7
8
9
10
11
12
public class StringBuffer01 {
public static void main(String[] args) {
//1. StringBuffer 的直接父类 是 AbstractStringBuilder
// 2. StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
// 3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是 final
// 该 value 数组存放 字符串内容,引出存放在堆中的
// 4. StringBuffer 是一个 final 类,不能被继承
// 5. 因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除)
// 不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
StringBuffer stringBuffer = new StringBuffer();
}
}

String VS StringBuffer

image-20220823222850921

String 和 StringBuffer 相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StringAndStringBuffer {
public static void main(String[] args) {
// String -> StringBuffer
String str = "abc";
//方式 1 使用构造器
// 注意: 返回的是 StringBuffer 对象,对 str 本身没有影响
// 大小 str.length + 16
StringBuffer stringBuffer = new StringBuffer(str);
//方式 2 使用的是 append 方法
// 空的Stringbuffer大小 16
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1.append(str);

// StringBuffer-->String
StringBuffer sbuf = new StringBuffer("abc");
//方式 1 使用 StringBuffer 提供的 toString 方法
String str1 = sbuf.toString();
//方式 2: 使用构造器来搞定
String s = new String(sbuf);
}
}

StringBuffer 类常见方法

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
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer s = new StringBuffer("hello");

//增
s.append(',');// "hello,"
s.append("张三丰");//"hello,张三丰"
// 默认会把其他类型转为字符串
s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏 100true10.5"
System.out.println(s);//"hello,张三丰赵敏 100true10.5"

//删
// 删除索引为>=start && <end 处的字符
// 解读: 删除 11~14 的字符 [11, 14)
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏 true10.5"

//改
//,使用 周芷若 替换 索引 9-11 的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若 true10.5"

//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6

//插
//在索引为 9 的位置插入 "赵敏",原来索引为 9 的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若 true10.5"

//长度
System.out.println(s.length());//22
System.out.println(s);
}
}

image-20220823225727282

image-20220823225802280

1
2
3
4
5
6
7
8
9
10
11
12
public class StringBufferExercise02 {
public static void main(String[] args) {
//要求:价格的小数点前面每三位用逗号隔开, 在输出。
String price = "123456875.988";

StringBuffer stringBuffer = new StringBuffer(price);
for (int i = stringBuffer.indexOf(".") -3; i > 0; i -= 3) {
stringBuffer.insert(i,",");
}
System.out.println(stringBuffer);
}
}

StringBuilder 类

image-20220823230649972

1
2
3
4
5
6
7
8
9
10
11
public class StringBuilder01 {
public static void main(String[] args) {
//1.StringBuilder的直接父类 是 AbstractStringBuilder
//2.实现了Serializable ,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)
//3.StringBuilder是final类,不能被继承
//4.StringBuilder对象字符序列仍然是存放在其父类 AbstractStringBuilder的 char[] value;
//因此,字符序列是堆中
// 5.StringBuilder的方法,没有做互斥的处理,即没有synchronized 关键字,因此在单线程的情况下使用
StringBuilder abc = new StringBuilder("abc");
}
}

StringBuilder 常用方法

image-20220823231932785

String、StringBuffer 和 StringBuilder 的比较

image-20220823232018470

image-20220823232036947

Math类

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
public class MathMethod {
public static void main(String[] args) {
//看看 Math 常用的方法(静态方法)
//1.abs 绝对值
int abs = Math.abs(-9);
System.out.println(abs);//9

//2.pow 求幂
double pow = Math.pow(2, 4);//2 的 4 次方
System.out.println(pow);//16

//3.ceil 向上取整,返回>=该参数的最小整数(转成 double);
double ceil = Math.ceil(3.9);
System.out.println(ceil);//4.0

//4.floor 向下取整,返回<=该参数的最大整数(转成 double)
double floor = Math.floor(4.001);
System.out.println(floor);//4.0

//5.round 四舍五入 Math.floor(该参数+0.5)
long round = Math.round(5.51);
System.out.println(round);//6

//6.sqrt 求开方
double sqrt = Math.sqrt(9.0);
System.out.println(sqrt);//3.0

//7.random 求随机数
// random 返回的是 0 <= x < 1 之间的一个随机小数
// 思考:请写出获取 a-b 之间的一个随机整数,a,b 均为整数 ,比如 a = 2, b=7
// 即返回一个数 x 2 <= x <= 7
// 解读 Math.random() * (b-a) 返回的就是 0 <= 数 <= b-a
// (1) (int)(a) <= x <= (int)(a + Math.random() * (b-a +1) )
// (2) 使用具体的数给小伙伴介绍 a = 2 b = 7
// (int)(a + Math.random() * (b-a +1) ) = (int)( 2 + Math.random()*6)
// Math.random()*6 返回的是 0 <= x < 6 小数
// 2 + Math.random()*6 返回的就是 2<= x < 8 小数
// (int)(2 + Math.random()*6) = 2 <= x <= 7
// (3) 公式就是 (int)(a + Math.random() * (b-a +1) )
for(int i = 0; i < 100; i++) {
System.out.println((int)(2 + Math.random() * (7 - 2 + 1)));
}

//max , min 返回最大值和最小值
int min = Math.min(1, 9);
int max = Math.max(45, 90);
System.out.println("min=" + min);
System.out.println("max=" + max);
}
}

Arrays类

常用方法

image-20220825183137002

image-20220825183200422

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class ArraysMethod01 {
public static void main(String[] args) {
Integer[] integers = {1,2,3,4,5};
//直接使用 Arrays.toString 方法,显示数组
System.out.println(Arrays.toString(integers));


//演示 sort 方法的使用
Integer arr[] = {1, -1, 7, 0, 89};
//1. 可以直接使用冒泡排序 , 也可以直接使用 Arrays 提供的 sort 方法排序
//2. 因为数组是引用类型,所以通过 sort 排序后,会直接影响到 实参 arr
//3. sort 重载的,也可以通过传入一个接口 Comparator 实现定制排序
//4. 调用 定制排序 时,传入两个参数 (1) 排序的数组 arr
//(2) 实现了 Comparator 接口的匿名内部类 , 要求实现 compare 方法
//5. 先演示效果,再解释
//6. 这里体现了接口编程的方式 , 看看源码,就明白
//源码分析
//(1) Arrays.sort(arr, new Comparator()
//(2) 最终到 TimSort 类的 private static <T> void binarySort(T[] a, int lo, int hi, int start,
//Comparator<? super T> c)()
//(3) 执行到 binarySort 方法的代码, 会根据动态绑定机制 c.compare()执行我们传入的
//匿名内部类的 compare ()
//while (left < right) {
// int mid = (left + right) >>> 1;
// if (c.compare(pivot, a[mid]) < 0)
// right = mid;
// else
// left = mid + 1;
// }
//(4) new Comparator() {
// @Override
// public int compare(Object o1, Object o2) {
// Integer i1 = (Integer) o1;
// Integer i2 = (Integer) o2;
// return i2 - i1;
// }
//}
//(5) public int compare(Object o1, Object o2) 返回的值>0 还是 <0
//会影响整个排序结果, 这就充分体现了 接口编程+动态绑定+匿名内部类的综合使用
//将来的底层框架和源码的使用方式,会非常常见
//Arrays.sort(arr); // 默认排序方法
//System.out.println(Arrays.toString(arr));

//定制排序
Arrays.sort(arr, new Comparator() {
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1; //从大到小
}
});
System.out.println(Arrays.toString(arr));

Integer[] arr1 = {1, 2, 90, 123, 567};
// binarySearch 通过二分搜索法进行查找,要求必须排好
//1. 使用 binarySearch 二叉查找
// 2. 要求该数组是有序的. 如果该数组是无序的,不能使用 binarySearch
// 3. 如果数组中不存在该元素,就返回 return -(low + 1); // key not found.
// 例如92不存在,如果存在就应该在第4个位置,所以查找92返回的是-4
System.out.println(Arrays.binarySearch(arr1, 92));

//copyOf 数组元素的复制
// 1. 从 arr 数组中,拷贝 arr.length 个元素到 newArr 数组中
// 2. 如果拷贝的长度 > arr.length 就在新数组的后面 增加 null
// 3. 如果拷贝长度 < 0 就抛出异常 NegativeArraySizeException
// 4. 该方法的底层使用的是 System.arraycopy()
Integer[] integers1 = Arrays.copyOf(arr1, arr1.length);
System.out.println(Arrays.toString(integers1));

//fill 数组元素的填充
Integer[] num = new Integer[]{9,3,2};
//1. 使用 99 去填充 num 数组,可以理解成是替换原理的元素
// 相当于把数组的所有元素都替换成99
Arrays.fill(num,99);
System.out.println(Arrays.toString(num));

//equals 比较两个数组元素内容是否完全一致
Integer[] arr2 = {1, 2, 90, 123};
// 1. 如果 arr 和 arr2 数组的元素一样,则方法 true;
// 2. 如果不是完全一样,就返回 false
System.out.println(Arrays.equals(arr1, arr2));

//asList 将一组值,转换成 list
// 1. asList 方法,会将 (2,3,4,5,6,1)数据转成一个 List 集合
// 2. 返回的 asList 编译类型 List(接口)
// 3. asList 运行类型 java.util.Arrays#ArrayList, 是 Arrays 类的
// 静态内部类 private static class ArrayList<E> extends AbstractList<E>
// implements RandomAccess, java.io.Serializable
List asList = Arrays.asList(2, 3, 4, 5, 6, 1);
System.out.println(asList);
}
}

自定义冒泡

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
package com.ep.arrays_;

import java.util.Arrays;
import java.util.Comparator;

/***
* @author dep
* @version 1.0
*/
public class ArraysBubbleCustom {
public static void main(String[] args) {
Integer[] arr = {1,5,6,8,3,10,4};
// bubble(arr); 默认的冒泡排序
bubble(arr, new Comparator() {
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2-i1;
}
});

System.out.println(Arrays.toString(arr));
}

// 冒泡排序
public static void bubble(Integer[] arr) {
Integer tmp = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length -1 -i; j++) {
if (arr[j] > arr[j+1]) {
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
// 冒泡排序+定制
// 使用接口Comparrator
public static void bubble(Integer[] arr, Comparator c) {
Integer tmp = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length -1 -i; j++) {
if (c.compare(arr[j],arr[j+1]) > 0) {
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
}

练习

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
public class ArraysExercise {
public static void main(String[] args) {
Books[] books = new Books[4];
books[0] = new Books("红楼梦", 100);
books[1] = new Books("金瓶梅新", 90);
books[2] = new Books("青年文摘 20 年", 5);
books[3] = new Books("java 从入门到放弃~", 300);
// 根据价格排序
Arrays.sort(books, new Comparator<Books>() {
@Override
public int compare(Books o1, Books o2) {
double differ = o1.price - o2.price;
if (differ > 0 ){
return 1;
} else if (differ < 0) {
return -1;
}else {
return 0;
}
}
});
System.out.println(Arrays.toString(books));
// 根据书名长度排序
Arrays.sort(books, new Comparator<Books>() {
@Override
public int compare(Books o1, Books o2) {
return o1.name.length() - o2.name.length();
}
});
System.out.println(Arrays.toString(books));
}
}
class Books {
public String name;
public double price;

public Books(String name, double price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

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

System类

image-20220825200415274

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
public class System01 {
public static void main(String[] args) {
//exit 退出当前程序
// System.out.println(1);
// //1. exit(0) 表示程序退出 //
// // 2. 0 表示一个状态 , 正常的状态
// System.exit(0);
// System.out.println(2);

//arraycopy :复制数组元素,比较适合底层调用,
// 一般使用 Arrays.copyOf 完成复制数组
Integer[] src = {1,2,3};
Integer[] dest = new Integer[3]; // dest 当前是 {0,0,0}

// * @param src the source array. 源数组
// * @param srcPos starting position in the source array. 从源数组的哪个索引位置开始拷贝
// * @param dest the destination array. 目标数组,即把源数组的数据拷贝到哪个数组
// * @param destPos starting position in the destination data. 把源数组的数据拷贝到 目标数组的哪个索引
// * @param length the number of array 从源数组拷贝多少个数据到目标数组
System.arraycopy(src,0,dest,0,dest.length);
System.out.println(Arrays.toString(dest));

//currentTimeMillens:返回当前时间距离 1970-1-1 的毫秒数
System.out.println(System.currentTimeMillis());
}
}

BigInteger 和 BigDecimal 类

image-20220825212734149

image-20220825212808235

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BigTnterge1 {
public static void main(String[] args) {
// long l = 12323333333333333333333333333333333333L;
BigInteger bigInteger = new BigInteger("12323232323232323232323232333333333");
System.out.println(bigInteger);
//1. 在对 BigInteger 进行加减乘除的时候,需要使用对应的方法,不能直接进行 + - * /
// 2. 可以创建一个 要操作的 BigInteger 然后进行相应操作
BigInteger i = new BigInteger("100");
BigInteger add = bigInteger.add(i); // 加
BigInteger subtract = bigInteger.subtract(i); // 减
BigInteger multiply = bigInteger.multiply(i); // 乘
BigInteger divide = bigInteger.divide(i); // 除
System.out.println(add);
System.out.println(subtract);
System.out.println(multiply);
System.out.println(divide);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BigDecimal1 {
public static void main(String[] args) {
//当我们需要保存一个精度很高的数时,double 不够用
double d = 199.999999999999999999999999999999999999;
//System.out.println(d); // 输出的是200.0

BigDecimal bigDecimal = new BigDecimal("199.999999999999999999999999999999");
System.out.println(bigDecimal);

BigDecimal i = new BigDecimal("1.1");
BigDecimal add = bigDecimal.add(i);
BigDecimal subtract = bigDecimal.subtract(i);
BigDecimal multiply = bigDecimal.multiply(i);
//BigDecimal divide = bigDecimal.divide(i); //可能抛出异常 ArithmeticException
//在调用 divide 方法时,指定精度即可. BigDecimal.ROUND_CEILING
// 如果有无限循环小数,就会保留 分子 的精度
BigDecimal divide = bigDecimal.divide(i, BigDecimal.ROUND_UP);
System.out.println(add);
System.out.println(subtract);
System.out.println(multiply);
System.out.println(divide);
}
}

日期类

第一代日期类Date

image-20220825214854184

image-20220825214915267

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
public class Date0 {
public static void main(String[] args) throws ParseException {
// 1. 获取当前系统时间
// 2. 这里的 Date 类是在 java.util 包
// 3. 默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换
Date d1 = new Date(); //获取当前系统时间
System.out.println("当前日期=" + d1); // 当前日期=Thu Aug 25 21:50:59 CST 2022
Date d2 = new Date(9234567);//通过指定毫秒数得到时间
System.out.println("d2=" + d2); // d2=Thu Jan 01 10:33:54 CST 1970

//1. 创建 SimpleDateFormat 对象,可以指定相应的格式
// 2. 这里的格式使用的字母是规定好,不能乱写
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format = sdf.format(d1); // // format:将日期转换成指定格式的字符串
System.out.println(format); // 2022年08月25日 09:52:45 星期四

//1. 可以把一个格式化的 String 转成对应的 Date
// 2. 得到 Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
// 3. 在把 String -> Date , 使用的 sdf 格式需要和你给的 String 的格式一样,否则会抛出转换异常
String s = "1996年01月01日 10:20:30 星期一";
Date date = sdf.parse(s);
System.out.println(date);
}
}

第二代日期类Calendar

image-20220825220104750

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
public class Calender0 {
public static void main(String[] args) {
//1. Calendar 是一个抽象类, 并且构造器是 private
// 2. 可以通过 getInstance() 来获取实例
// 3. 提供大量的方法和字段提供给程序员
//4. Calendar 没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
// 5. 如果我们需要按照 24 小时进制来获取时间, Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance(); //创建日历类对象 比较简单,自由
System.out.println("c=" + c);

//2.获取日历对象的某个日历字段
System.out.println("年:" + c.get(Calendar.YEAR));
// 这里为什么要 + 1, 因为 Calendar 返回月时候,是按照 0 开始编号
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));
//Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println(c.get(Calendar.YEAR) + "年" + (c.get(Calendar.MONTH)+ 1) + "月" + c.get(Calendar.DAY_OF_MONTH)
+ "日" + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) +":"+ c.get(Calendar.SECOND)
);
}
}

第三代日期类LocalDate

image-20220825222301649

image-20220825222316962

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
public class LocalDate0 {
public static void main(String[] args) {
//第三代日期
//1. 使用 now() 返回表示当前日期时间的 对象
LocalDateTime now = LocalDateTime.now(); // //LocalDate.now();//LocalTime.now()
System.out.println(now);

//2. 使用 DateTimeFormatter 对象来进行格式化
// 创建 DateTimeFormatter 对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(now);
System.out.println(format);

LocalDateTime ldt = LocalDateTime.now();
System.out.println("年=" + ldt.getYear());
System.out.println("月=" + ldt.getMonth()); // AUGUST
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());

LocalDate now1 = LocalDate.now(); //可以获取年月日
LocalTime now2 = LocalTime.now();//获取到时分秒

//提供 plus 和 minus 方法可以对当前时间进行加或者减
// 看看 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));

}
}

DateTimeFormatter 格式日期类

image-20220825223044695

Instant 时间戳

image-20220825223122302

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Instant0 {
public static void main(String[] args) {
//1.通过 静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now); // 2022-08-25T14:32:22.988Z

//2. 通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);
System.out.println(date); // Thu Aug 25 22:34:08 CST 2022
//3. 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant();
System.out.println(instant); // 2022-08-25T14:34:08.239Z
}
}

image-20220825223448311

集合

数组的不足

image-20220826213554328

集合的优点

image-20220826213617872

集合的框架体系

image-20220826213643751

image-20220826213658259

  1. 集合主要是两组(单列集合 , 双列集合)

  2. Collection 接口有两个重要的子接口 List Set , 他们的实现子类都是单列集合

  3. Map 接口的实现子类 是双列集合,存放的 K-V

Collection 接口和常用方法

image-20220826213754876

由于接口不能直接实例化,采用ArrayList实现类来展示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
33
34
35
36
37
38
39
40
41
42
public class CollectionMethod {
public static void main(String[] args) {
List list = new ArrayList();
// add:添加单个元素
list.add("abc");
list.add(100);
list.add(true);
System.out.println(list);

// remove:删除指定元素
// list.remove(0);//删除第一个元素
list.remove("abc"); // 删除指定值的元素
//System.out.println(list);

// contains:查找元素是否存在
System.out.println(list.contains(true));

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

// isEmpty:判断是否为空
System.out.println(list.isEmpty());

// clear:清空
list.clear(); // 清空列表
System.out.println(list);

// addAll:添加多个元素
ArrayList arrayList = new ArrayList();
arrayList.add("李四");
arrayList.add("张三");
list.addAll(arrayList);
System.out.println(list);

// containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(arrayList));

// removeAll:删除多个元素
list.removeAll(arrayList);
System.out.println(list);
}
}

Iterator(迭代器)

image-20220826215606295

image-20220826215624353

image-20220826215744548

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
public class CollectionIterator {
public static void main(String[] args) {
Book book = new Book("三国演义", "罗贯中");
Book book2 = new Book("水浒传","施耐庵");
Book book3 = new Book("红楼梦","曹雪芹");
Book book4 = new Book("水浒传","吴承恩");
Collection col = new ArrayList();
col.add(book);
col.add(book2);
col.add(book3);
col.add(book4);

//1. 先得到 col 对应的 迭代器
// 快捷键itit 查看快捷键列表ctrl+j
Iterator iterator = col.iterator();
while (iterator.hasNext()) { //判断是否还有数据
Object next = iterator.next(); //返回下一个元素,类型是 Object
System.out.println(next);
}
//3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素
// iterator.next();//NoSuchElementException
// 4. 如果希望再次遍历,需要重置我们的迭代器
System.out.println("=========================");
iterator = col.iterator(); // 遍历第二次需要重置迭代器
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}

}
}

增强for循环

image-20220826221118627

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CollectionFor {
public static void main(String[] args) {
Book book = new Book("三国演义", "罗贯中");
Book book2 = new Book("水浒传","施耐庵");
Book book3 = new Book("红楼梦","曹雪芹");
Book book4 = new Book("水浒传","吴承恩");
Collection col = new ArrayList();
col.add(book);
col.add(book2);
col.add(book3);
col.add(book4);

// 快捷键 I
// 增强for循环,底层还是迭代器
// 不仅可以遍历集合还可以遍历数组
for (Object o : col) {
System.out.println(o);
}
}
}

List 接口

image-20220826222634561

常用方法

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
public class ListMethod {
public static void main(String[] args) {
List list = new ArrayList();
list.add("张三");
list.add("李四");
// void add(int index, Object ele):在 index 位置插入 ele 元素
list.add(1,"王五"); // 在索引为1的前面插入元素
System.out.println(list);

// boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来
ArrayList arrayList = new ArrayList();
arrayList.add("java");
arrayList.add("c++");
list.addAll(1,arrayList);
System.out.println(list);

// Object get(int index):获取指定 index 位置的元素
System.out.println(list.get(0)); // 获取索引为0的元素

// int indexOf(Object obj):返回 obj 在集合中首次出现的位置
System.out.println(list.indexOf("java"));
list.add("java");
// int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
System.out.println(list.lastIndexOf("java"));

// Object remove(int index):移除指定 index 位置的元素,并返回此元素
Object remove = list.remove(0);
System.out.println(remove);

// Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.
list.set(2,"python");
System.out.println(list);

// List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
// 注意返回的子集合 fromIndex <= subList < toIndex
List list1 = list.subList(0, 3);// 返回的是索引为0,1,2的元素组成的数组,不包括索引为3的
System.out.println(list1);
}
}

三种遍历方式

image-20220826224110995

练习

image-20220826224652623

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
public class ListExercise {
public static void main(String[] args) {
List list = new ArrayList();
// List list = new LinkedList();
// List list = new Vector();
list.add(new Book("红楼梦", "曹雪芹", 100));
list.add(new Book("西游记", "吴承恩", 10));
list.add(new Book("水浒传", "施耐庵", 19));
list.add(new Book("三国", "罗贯中", 80));
bubbleSort(list);
for (Object o : list) {
System.out.println(o);
}
}
// 排序
public static void bubbleSort(List list) {
int length = list.size();
for (int i = 0; i < length-1; i++) {
for (int j = 0; j < length-1-i; j++) {
Book book1 = (Book) list.get(j);
Book book2 = (Book) list.get(j+1);
if (book1.getPrice() > book2.getPrice()) { //交换
list.set(j, book2);
list.set(j+1,book1);
}

}

}
}
}

ArrayList

image-20220826230156755

image-20220826230228567

底层源码分析

调试的时候按F7进入方法内部

image-20220826232250175

image-20220826232014828

image-20220826232029108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@SuppressWarnings({"all"})
public class ArrayListSource {
public static void main(String[] args) {
//解读源码
//注意,注意,注意,Idea 默认情况下,Debug 显示的数据是简化后的,如果希望看到完整的数据
//需要做设置.
//使用无参构造器创建 ArrayList 对象
ArrayList list = new ArrayList();
//ArrayList list = new ArrayList(8);

//使用 for 给 list 集合添加 1-10 数据
for (int i = 1; i <= 10; i++) {
list.add(i);
}

//使用 for 给 list 集合添加 11-15 数据
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
}

image-20220826233127055

image-20220826233357080

默认大小是10 (DEFAULT_CAPACITY)

image-20220826233447490

image-20220826233652874

image-20220826233944197

Vector

image-20220827213833767

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
package com.ep.list_;

import java.util.Vector;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class vector_ {
public static void main(String[] args) {
//无参构造器
//有参数的构造
Vector vector = new Vector(8);
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector=" + vector);

//1. new Vector() 底层
/* public Vector() {
this(10);
}

补充:如果是 Vector vector = new Vector(8);
走的方法:
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}

\2. vector.add(i)
2.1 //下面这个方法就添加数据到 vector 集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}

2.2 //确定是否需要扩容 条件 : minCapacity - elementData.length>0
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

2.3 //如果 需要的数组大小 不够用,就扩容 , 扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
//就是扩容两倍.
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/
}
}

Vector 和 ArrayList 的比较

image-20220827215544426

LinkedList

image-20220827223700694

image-20220827223720322

模拟双向链表

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
78
79
package com.ep.list_;

/***
* @author dep
* @version 1.0
*/
public class LinkedList01 {
public static void main(String[] args) {
//模拟一个简单的双向链表
Node aaa = new Node("aaa");
Node bbb = new Node("bbb");
Node ccc = new Node("ccc");

// 连接三个结点,形成双向链表
aaa.next = bbb;
bbb.next = ccc;

ccc.pre = bbb;
bbb.pre = aaa;

Node first = aaa; // first指向双向链表的第一个结点
Node last = ccc; // last指向双向链表的最后一个结点

// 正向输出
Node p = first;
while(p!=null) {
System.out.println(p);
p = p.next;
}

System.out.println("=========");
// 逆序输出
Node p1 = last;
while (p1 != null) {
System.out.println(p1);
p1 = p1.pre;
}

// 插入 aaa和bbb之间插入111
Node newNode = new Node("111");
// newNode.next = bbb;
// newNode.pre = aaa;
// aaa.next = newNode;
// bbb.pre = newNode;
// 只知道一个结点aaa
newNode.next = aaa.next;
newNode.pre = aaa;
aaa.next.pre = newNode;
aaa.next = newNode;

System.out.println("=======");
// 正向输出
Node p2 = first;
while(p2!=null) {
System.out.println(p2);
p2 = p2.next;
}

}
}

// 定义一个Node类,Node对象 表示双向链表的一个结点
class Node {
public Object item; // 真正存放数据
public Node next; // 指向最后一个结点
public Node pre; // 指向前一个结点

public Node(Object item) {
this.item = item;
}

@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
}

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.ep.list_;

import java.util.Iterator;
import java.util.LinkedList;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class LinkedListCRUD {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
System.out.println("linkedList=" + linkedList);

//演示一个删除结点的
linkedList.remove(); // 这里默认删除的是第一个结点
//linkedList.remove(2);
System.out.println("linkedList=" + linkedList);

//修改某个结点对象
linkedList.set(1, 999);
System.out.println("linkedList=" + linkedList);

//得到某个结点对象
//get(1) 是得到双向链表的第二个对象
Object o = linkedList.get(1);
System.out.println(o);//999

//因为 LinkedList 是 实现了 List 接口, 遍历方式
System.out.println("===LinkeList 遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("next=" + next);
}

System.out.println("===LinkeList 遍历增强 for====");
for (Object o1 : linkedList) {
System.out.println("o1=" + o1);
}

System.out.println("===LinkeList 遍历普通 for====");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}

//源码阅读.
/* 1. LinkedList linkedList = new LinkedList();
public LinkedList() {}
2. 这时 linkeList 的属性 first = null last = null
3. 执行 添加
public boolean add(E e) {
linkLast(e);
return true;
}

4.将新的结点,加入到双向链表的最后
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
*/

/*读源码 linkedList.remove(); // 这里默认删除的是第一个结点
1. 执行 removeFirst
public E remove() {
return removeFirst();
}
2. 执行
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
3. 执行 unlinkFirst, 将 f 指向的双向链表的第一个结点拿掉
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
*/
}
}

ArrayList 和 LinkedList 比较

image-20220828163207217

Set接口

image-20220828164028892

和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样

Set 接口的遍历方式

image-20220828164157606

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
package com.ep.set_;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class SetMethod {
public static void main(String[] args) {
//1. 以 Set 接口的实现类 HashSet 来讲解 Set 接口的方法
// 2. set 接口的实现类的对象(Set 接口对象), 不能存放重复的元素, 可以添加一个 null /
// /3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
// 4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他是固定的.
Set set = new HashSet();
set.add("tom");
set.add("jack");
set.add("jack"); // 只会添加一次
set.add("zhangsan");
set.add(true);
set.add(null);
set.add(null);

System.out.println(set);

//遍历
// 方式 1: 使用迭代器
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

set.remove(null);

//方式 2: 增强 for
System.out.println("=====增强 for====");
for (Object o : set) {
System.out.println(o);
}
//set 接口对象,不能通过索引来获取


}
}

HashSet

image-20220828165816664

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HashSet_ {
public static void main(String[] args) {
//1. 构造器走的源码
/*
* public HashSet() {
* map = new HashMap<>();
* }
*/
// 2. HashSet 可以存放 null ,但是只能有一个 null,即元素不能重复 */
HashSet hashSet = new HashSet();

}
}
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
package com.ep.set_;


import java.util.HashSet;

/***
* @author dep
* @version 1.0
*/
public class HashSet01 {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
//说明
// 1. 在执行 add 方法后,会返回一个 boolean 值
// 2. 如果添加成功,返回 true, 否则返回 false
// 3. 可以通过 remove 指定删除哪个对象
System.out.println(hashSet.add("tom")); // true
System.out.println(hashSet.add("tom")); // false
System.out.println(hashSet.add("jack")); // true
System.out.println(hashSet.add("tom")); //false

System.out.println(hashSet.size()); // 2

//4 Hashset 不能添加相同的元素/数据?
System.out.println(hashSet.add("lisi")); // true
System.out.println(hashSet.add("lisi")); // false
System.out.println(hashSet.add(new Dog("lisi"))); // true
System.out.println(hashSet.add(new Dog("lisi"))); // true

// 这个确加入不了,需要看源码
System.out.println(hashSet.add(new String("abc"))); // true
System.out.println(hashSet.add(new String("abc"))); // false
}
}
class Dog{
private String name;

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

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

HashSet 底层机制

image-20220828171721252

image-20220828171746198

image-20220828171900912

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package com.ep.set_;

import java.util.HashSet;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class HashSetSource {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add("java");//到此位置,第 1 次 add 分析完毕.
hashSet.add("php");//到此位置,第 2 次 add 分析完毕
hashSet.add("java");
System.out.println("set=" + hashSet);

/*
HashSet 的源码解读
1. 执行 HashSet()
public HashSet() {
map = new HashMap<>();
}

2. 执行 add()
public boolean add(E e) {//e = "java"
return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
}

3.执行 put() , 该方法会执行 hash(key) 得到 key 对应的 hash 值 算法 h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {//key = "java" value = PRESENT 共享
return putVal(hash(key), key, value, false, true);
}

4.执行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量
//table 就是 HashMap 的一个数组,类型是 Node[]
//if 语句表示如果当前 table 是 null, 或者 大小=0
//就是第一次扩容,到 16 个空间.
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//(1)根据 key,得到 hash 去计算该 key 应该存放到 table 表的哪个索引位置
//并把这个位置的对象,赋给 p
//(2)判断 p 是否为 null
//(2.1) 如果 p 为 null, 表示还没有存放元素, 就创建一个 Node (key="java",value=PRESENT)
//(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建
Node<K,V> e; K k; //
//如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样
//并且满足 下面两个条件之一:
//(1) 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象
//(2) p 指向的 Node 结点的 key 的 equals() 和准备加入的 key 比较后相同
//就不能加入
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//再判断 p 是不是一颗红黑树,
//如果是一颗红黑树,就调用 putTreeVal , 来进行添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//如果 table 对应索引位置,已经是一个链表, 就使用 for 循环比较
//(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后

//注意在把元素添加到链表后,立即判断 该链表是否已经达到 8 个结点
// 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
//注意,在转成红黑树时,要进行判断, 判断条件
//if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
// resize();
//如果上面条件成立,先 table 扩容.
//只有上面条件不成立时,才进行转成红黑树
//(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接 break
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}

}
++modCount;
//size 就是我们每加入一个结点 Node(k,v,h,next), size++
if (++size > threshold)
resize();//扩容
afterNodeInsertion(evict);
return null;
}
*/
}
}

image-20220828204553405

练习

image-20220828204622340

使用alt+enter生成equals和hashCode方法

image-20220828204739405

image-20220828205043256

image-20220828205125753

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
package com.ep.set_;

import java.util.HashSet;
import java.util.Objects;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class HashSetExercise {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
System.out.println(hashSet.add(new Employee("zhangsan", 18))); //true
System.out.println(hashSet.add(new Employee("lisi", 18))); // true
System.out.println(hashSet.add(new Employee("zhangsan", 18))); // false
}
}

class Employee {
private String name;
private Integer age;

public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) && Objects.equals(age, employee.age);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

LinkedHashSet

插入顺序和输出顺序相同

image-20220828212101174

image-20220828212135551

练习

image-20220828212615793

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
package com.ep.set_;

import java.util.LinkedHashSet;
import java.util.Objects;

/***
* @author dep
* @version 1.0
*/
public class LinkedHashSet0 {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new Car("奥拓", 1000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//OK
linkedHashSet.add(new Car("法拉利", 10000000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//加入不了
linkedHashSet.add(new Car("保时捷", 70000000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//加入不了
System.out.println("linkedHashSet=" + linkedHashSet);
}
}

class Car {
private String name;
private double price;

public Car(String name, double price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

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

// 重写equals方法和hashcode方法,要使两个对象的name和age相同时返回true

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
}

@Override
public int hashCode() {
return Objects.hash(name, price);
}
}

TreeSet

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
package com.ep.set_;

import java.util.Comparator;
import java.util.TreeSet;

/***
* @author dep
* @version 1.0
*/
public class TreeSet_ {
public static void main(String[] args) {
//1. 当我们使用无参构造器,创建 TreeSet 时,仍然是无序的
// TreeSet treeSet = new TreeSet();
// treeSet.add("abc");
// treeSet.add("ab");
// treeSet.add("a");
// treeSet.add("abcd");
// treeSet.add("b");
// System.out.println(treeSet);

// 2. 希望添加的元素,按照字符串大小来排序
// 3. 使用 TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
// 并指定排序规则
/*
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t; //动态绑定到我们的匿名内部类(对象)
compare cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null);
}
*/
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// return ((String)o1).compareTo( (String)o2 );
return ((String)o1).length() - ((String)o2).length(); // 如果是这种情况长度相等就添加不进去了
}
});
treeSet.add("a");
treeSet.add("edhhd");
treeSet.add("abc");
treeSet.add("ed");
treeSet.add("ab"); // 如果比较方法是比较长度,因为上面有长度为2的,所以这个添加不进去
System.out.println(treeSet);

}
}

Map接口

image-20220828214717364

和HashSet一样也是无序的

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
package com.ep.map_;

import java.util.HashMap;
import java.util.Map;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class MapMethod {
public static void main(String[] args) {
//解读 Map 接口实现类的特点, 使用实现类 HashMap
// 1. Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
// 2. Map 中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中
// 3. Map 中的 key 不允许重复,原因和 HashSet 一样,前面分析过源码.
// 4. Map 中的 value 可以重复
// 5. Map 的 key 可以为 null, value 也可以为 null ,注意 key 为 null,只能有一个,value 为 null ,可以多个
// 6. 常用 String 类作为 Map 的 key
// 7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
Map map = new HashMap();
map.put("aaa","aaa");
map.put("bbb","bbb");
map.put("aaa",111); //会修改aaa的值
map.put(null,null);
map.put(null,"abc");
map.put(new Object(),"object");
map.put(1,"111");

// 通过 get 方法,传入 key ,会返回对应的 value
System.out.println(map.get("aaa"));
System.out.println(map.get("as")); // 不存在的key会返回null

System.out.println(map);
}
}

image-20220828220646493

常用方法

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
package com.ep.map_;

import java.util.HashMap;
import java.util.Map;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class MapMethod {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超", new Book("", 100));//OK
map.put("邓超", "孙俪");//替换-> 一会分析源码
map.put("王宝强", "马蓉");//OK
map.put("宋喆", "马蓉");//OK
map.put("刘令博", null);//OK
map.put(null, "刘亦菲");//OK
map.put("鹿晗", "关晓彤");//OK
System.out.println("map=" + map);

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

// get:根据键获取值
System.out.println(map.get("邓超"));

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

// clear:清除 k-v
map.clear();

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

// containsKey:查找键是否存在
System.out.println(map.containsKey("邓超"));
}
}

class Book {
private String name;
private double price;

public Book(String name, double price) {
this.name = name;
this.price = price;
}
}

遍历

image-20220828223242297

image-20220828223258475

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
package com.ep.map_;

import java.util.*;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class MapFor {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超", "孙俪");
map.put("王宝强", "马蓉");
map.put("宋喆", "马蓉");
map.put("刘令博", null);
map.put(null, "刘亦菲");
map.put("鹿晗", "关晓彤");

//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keySet = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keySet) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next + "-" + map.get(next));
}

//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
// (1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}

//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet(); // EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry mapEntry = (Map.Entry) entry;
System.out.println(mapEntry.getKey() + "-" + mapEntry.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object next = iterator2.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
// 向下转型 Map.Entry
Map.Entry mapEntry = (Map.Entry) next;
System.out.println(mapEntry.getKey() + "-" + mapEntry.getValue());
}

}
}

练习

image-20220828224958236

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.ep.map_;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/***
* @author dep
* @version 1.0
*/
public class MapExercise {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
Employee e1 = new Employee(1, "张三", 19000);
Employee e2 = new Employee(2, "李四", 1200);
Employee e3 = new Employee(3, "王五", 200000);
hashMap.put(e1.getId(), e1);
hashMap.put(e2.getId(), e2);
hashMap.put(e3.getId(), e3);

// 方式一
System.out.println("------方式一:keySet---------");
Set keySet = hashMap.keySet();
for (Object key :keySet) {
Employee e = (Employee) hashMap.get(key);
if(e.getSalary() > 18000){
System.out.println(e);
}
}

// 方式二
System.out.println("------方式二:values---------");
for (Object value : hashMap.values()) {
Employee e = (Employee) value;
if(e.getSalary() > 18000){
System.out.println(value);
}
}
//方式三
System.out.println("------方式三:entrySet---------");
for (Object entry : hashMap.entrySet()) {
Map.Entry e = (Map.Entry) entry;
Employee em = (Employee) e.getValue();
if(em.getSalary() > 18000){
System.out.println(em);
}
}


}
}

class Employee {
private Integer id;
private String name;
private double salary;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

public Employee(Integer id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}

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

HashMap

image-20220830213411144

底层机制及源码剖析

image-20220830213635164

image-20220830213723110

HashTable

image-20220830221029155

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HashTableExercise {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable();

hashtable.put("abc",123);
// 不可以添加key和value为空的数据
//hashtable.put(null,123); // NullPointerException
// hashtable.put("aaa",null); // NullPointerException
hashtable.put("aaaa",111);
hashtable.put("abc","abc"); // 会替换
System.out.println(hashtable);
}
}

image-20220830221857820

Hashtable 和 HashMap 对比

image-20220830221502752

Properties

image-20220830222827954

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Properties_ {
public static void main(String[] args) {
//1. Properties 继承 Hashtable
// 2. 可以通过 k-v 存放数据,当然 key 和 value 不能为 null
Properties properties = new Properties();
properties.put("abc","abc");
// properties.put(null,"abc"); // NullPointerException
// properties.put("aaa",null); // NullPointerException
properties.put("abc","aaa"); // 替换

//通过 k 获取对应值
System.out.println(properties.get("abc"));
System.out.println(properties.getProperty("abc"));

// 删除
properties.remove("abc");
}
}

TreeMap

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
package com.ep.map_;

import java.util.Comparator;
import java.util.TreeMap;

/***
* @author dep
* @version 1.0
*/
public class TreeMap_ {
public static void main(String[] args) {
//使用默认的构造器,创建 TreeMap, 是无序的(也没有排序)
// 如果key都是字符串,默认是按照字符串首字母排序的
// TreeMap treeMap = new TreeMap();
// treeMap.put("a","a");
// treeMap.put("ddd","d");
// treeMap.put("ab","a");
// treeMap.put("abc","a");
// treeMap.put("bbbbb","a");
// System.out.println(treeMap);

//按照传入的 k(String) 的大小进行排序
TreeMap treeMap = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// return ((String) o1).compareTo((String) o2);
return ((String) o1).length() - ((String) o2).length();
}
});
treeMap.put("a", "a");
treeMap.put("ddd", "d");
treeMap.put("ab", "a");
treeMap.put("abc", "a"); // 根据长度排序,ddd的值将会被修改为a
treeMap.put("bbbbb", "a");
System.out.println(treeMap);

// 解读源码
/***
1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 调用 put 方法
2.1 第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
2.2 以后添加
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加
return t.setValue(value); // 替换值
} while (t != null);
}
*/
}
}

如何选择

  1. 先判断存储的类型(一组对象[单列]或一组键值对[双列)

  2. 一组对象[单列]:Collection接口

    • 允许重复:List

      增删多: LinkedList[底层维护了一个双向链表]

      改查多: ArrayList[底层维护Object类型的可变数组]

    • 不允许重复:Set
      无序:HashSet [底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)]

      排序:TreeSet

      插入和取出顺序一致:LinkedHashSet,维护数组+双向链表

  3. 一组键值对[双列]:Map

    键无序: HashMap [底层是:哈希表 jdk7: 数组+链表,jdk8:数组+链表+红黑树]

    键排序:TreeMap

    键插入和取出顺序一致:LinkedHashMap

    读取文件 Properties

image-20220830224215152

Collections 工具类

image-20220831223115154

image-20220831223132698

image-20220831224421741

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.ep.collections_;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/***
* @author dep
* @version 1.0
*/
@SuppressWarnings({"all"})
public class CollectionMethod {
public static void main(String[] args) {
//创建 ArrayList 集合,用于测试.
List list = new ArrayList();
list.add("aaaa");
list.add("aaaa");
list.add("aaaa");
list.add("bbb");
list.add("cc");
list.add("d");
list.add("eeee");
System.out.println("原list="+list);

// reverse(List):反转 List 中元素的顺序
Collections.reverse(list);
System.out.println("逆序:"+list);

// shuffle(List):对 List 集合元素进行随机排序
for (int i = 0; i < 5; i++) {
Collections.shuffle(list);
System.out.println("随机排序:" + list);
}

// sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
Collections.sort(list);
System.out.println("自然排序:" + list);

// sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
// 我们希望按照 字符串的长度大小排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// if (o1 instanceof String && o2 instanceof String)
return ((String) o2).length() - ((String) o1).length();
// else {
// return 0;
// }
}
});
System.out.println("定制排序:" + list);

// swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Collections.swap(list, 0 ,list.size() - 1);
System.out.println("交换后:"+list);

//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
System.out.println("最大值:" + Collections.max(list));

//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
// 比如,我们要返回长度最大的元素
String max = Collections.max(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
});
System.out.println("定制排序最大值:" + max);


//Object min(Collection)
// Object min(Collection,Comparator)
// 上面的两个方法,参考 max 即可

//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
System.out.println(Collections.frequency(list, "aaaa"));

//void copy(List dest,List src):将 src 中的内容复制到 dest 中
//为了完成一个完整拷贝,我们需要先给 dest 赋值,大小和 list.size()一样
ArrayList dest = new ArrayList();
for(int i = 0; i < list.size(); i++) {
dest.add("");
}
//拷贝
Collections.copy(dest, list);
System.out.println("dest=" + dest);

//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
Collections.replaceAll(list,"aaaa","AAAA");
System.out.println(list);

}
}

集合作业练习

练习1:

image-20220831225052686

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
78
79
80
81
82
83
84
package com.ep.collection_.homework;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

/***
* @author dep
* @version 1.0
*/
public class HomeWork1 {
public static void main(String[] args) {
News news1 = new News("新冠确诊病例超千万,数百万印度教信徒赴恒河\"圣浴\"引民众担忧");
News news2 = new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生");
ArrayList arrayList = new ArrayList();
arrayList.add(news1);
arrayList.add(news2);
Collections.reverse(arrayList); // 逆序
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
News news = (News) next;
// if (news.getTitle().length() > 15) {
// news.setTitle(news.getTitle().substring(0,15) + "...");
// }
// System.out.println(news);
System.out.println(processTitle(news.getTitle()));
}
}
// 专门处理标题
public static String processTitle(String title) {
if (title == null) {
return "";
}
if (title.length() > 15) {
return title.substring(0,15) + "...";
} else {
return title;
}
}
}

/**
* 按要求实现:
* (1)封装一个新闻类,包含标题和内容属性,提供get、set方法,重写toString方法,打印对象时只打印标题;
* (2)只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河“圣浴”引民众担忧
* 新闻二:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生
* (3)将新闻对象添加到ArrayList集合中,并且进行倒序遍历;
* (4)在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加“.….
* (5)在控制台打印遍历出经过处理的新闻标题;
*/
class News {
private String title;
private String Content;

public News(String title) {
this.title = title;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getContent() {
return Content;
}

public void setContent(String content) {
Content = content;
}

@Override
public String toString() {
return "News{" +
"title='" + title + '\'' +
'}';
}

}

练习2

image-20220831230245263

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
package com.ep.collection_.homework;

import java.util.ArrayList;
import java.util.List;

/***
* @author dep
* @version 1.0
*/
public class HomeWork2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
Car car = new Car("宝马", 400000);
Car car2 = new Car("宾利", 900000);
list.add(car); // 添加元素
list.add(car2);
list.add(car);
System.out.println(list);

list.remove(0); // 移除元素

System.out.println(list.contains(car)); //包含某个元素

System.out.println("元素个数:" + list.size()); // 元素个数

System.out.println("是否为空:" + list.isEmpty()); // 是否为空

list.clear(); // 清空
System.out.println(list);

List list2 = new ArrayList();
list2.add(car2);
list2.add(car);
list.addAll(list2);
System.out.println(list);
list.removeAll(list2);
}
}

class Car{
private String name;
private double price;

public Car(String name, double price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

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

练习3

image-20220831231108216

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
package com.ep.collection_.homework;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/***
* @author dep
* @version 1.0
*/
public class HomeWork3 {
public static void main(String[] args) {

HashMap map = new HashMap();
map.put("jack",650);
map.put("tom",1200);
map.put("smith",2900);

map.put("jack",2600); // 修改jack工资

Set entrySet = map.entrySet();
for (Object o : entrySet) {
Map.Entry entry = (Map.Entry) o;
map.put(entry.getKey(),(Integer) entry.getValue() + 100); // 所有员工工资加100
}
System.out.println(map);

// 遍历所有员工
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));

}

// 遍历所有工资
for (Object value : map.values()) {
System.out.println(value);
}


}
}

4.HashSet和TreeSet如何实现去重

image-20220831232511279

5.代码分析

image-20220831232752984

会报ClassCastException异常

解决方法:

Person类实现ComParable接口,实现里面的方法

image-20220831233551240

6.代码分析

image-20220831233757367

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
package com.ep.collection_.homework;

import java.util.HashSet;
import java.util.Objects;

/***
* @author dep
* @version 1.0
*/
public class HomeWork6 {
public static void main(String[] args) {
Person person1 = new Person(1001,"AA");
Person person2 = new Person(1002,"BB");
HashSet set = new HashSet();
set.add(person1);
set.add(person2);
System.out.println(set); // [Person{id=1002, name='BB'}, Person{id=1001, name='AA'}]
person1.setName("CC");
System.out.println(set); //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
System.out.println( set.remove(person1)); // false 因为p1.name已经被修改了
set.add(new Person(1001,"CC")); // 还能添加成功
System.out.println(set); // [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
set.add(new Person(1001,"AA"));
System.out.println(set); // [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
}
}
class Person{
private Integer id;
private String name;

public Person(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) && Objects.equals(name, person.name);
}

@Override
public int hashCode() {
return Objects.hash(id, name);
}

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

image-20220901214102686

泛型

使用传统方法的问题分析

image-20220901220939831

基本使用

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
package com.ep.generic;

import java.util.ArrayList;

/***
* @author dep
* @version 1.0
*/
public class Generic01 {
public static void main(String[] args) {
//1. 当我们 ArrayList<Dog> 表示存放到 ArrayList 集合中的元素是 Dog 类型 (细节后面说...)
// 2. 如果编译器发现添加的类型,不满足要求,就会报错 /
// /3. 在遍历的时候,可以直接取出 Dog 类型而不是 Object
// 4. public class ArrayList<E> {} E 称为泛型,那么 Dog->E
ArrayList<Dog> arrayList = new ArrayList<>();
arrayList.add(new Dog("大黄",1));
arrayList.add(new Dog("小黄", 2));

for (Dog dog : arrayList) {
System.out.println(dog);
}

}
}
class Dog {
private String name;
private Integer age;

public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

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

泛型的好处

image-20220901221425734

泛型介绍

image-20220901221505032

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
package com.ep.generic;

/***
* @author dep
* @version 1.0
*/
public class Generic02 {
public static void main(String[] args) {
//注意,特别强调: E 具体的数据类型在定义 Person 对象的时候指定,即在编译期间,就确定 E 是什么类型
Person<String> person = new Person<>("aaa");
person.t();
}
}
//泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型
class Person<E> {
E name; //E 表示 name 的数据类型, 该数据类型在定义 Person 对象的时候指定,即在编译期间,就确定 E 是什么类型

public Person(E name) { //E 也可以是参数类型
this.name = name;
}
public E getName() { //返回类型使用 E
return name;
}
public void t() {
System.out.println(name.getClass());
}
}

泛型使用细节

image-20220901223556132

  1. 给泛型指向数据类型是,要求是引用类型,不能是基本数据类型

  2. 在给泛型指定具体类型后,可以传入该类型或者其子类类型

  3. 泛型推荐简写

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
package com.ep.generic;

import java.util.ArrayList;
import java.util.List;

/***
* @author dep
* @version 1.0
*/
public class GenericDetail {
public static void main(String[] args) {
//1.给泛型指向数据类型是,要求是引用类型,不能是基本数据类型
ArrayList<String> arrayList = new ArrayList<>();
// ArrayList<int> ints = new ArrayList<int>(); 会报错

//因为 E 指定了 A 类型, 构造器传入了 new A() //在给泛型指定具体类型后,可以传入该类型或者其子类类型
P<A> A = new P<>(new A());
P<A> pa = new P<>(new B());

//3. 泛型的使用形式
ArrayList<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
// 在实际开发中,我们往往简写
// 推荐这种写法
ArrayList<String> arrayList1 = new ArrayList<>();

//4. 如果是这样写 泛型默认是Object
ArrayList arrayList2 = new ArrayList();//等价// ArrayList<Object> arrayList2 = new ArrayList<Object>();
}
}

class A{}
class B extends A{}
class P<E> {
E s;

public P(E s) {
this.s = s;
}

public void t() {
System.out.println(s.getClass());
}
}

自定义泛型类

image-20220902201451780

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.ep.generic;

/***
* @author dep
* @version 1.0
*/
public class CustomGeneric {
public static void main(String[] args) {
//T=Double, R=String, M=Integer
Tiger<Double,String,Integer> g = new Tiger<>("john");
g.setT(10.9); //OK
// g.setT("yy"); //错误,类型不对
// System.out.println(g);
Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object
g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是 Object 子类
System.out.println("g2=" + g2);
}
}
//1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
// 2, T, R, M 泛型的标识符, 一般是单个大写字母
// 3. 泛型标识符可以有多个.
// 4. 普通成员可以使用泛型 (属性、方法)
// 5. 使用泛型的数组,不能初始化
// 6. 静态方法中不能使用类的泛型
class Tiger<T,R,M> {
String name;
T t; //属性使用到泛型
R r;
M m;
//因为数组在 new 不能确定 T 的类型,就无法在内存开空间
T ts[];
// T ts[] = new T[10]; // 错误的,因为无法确定泛型的具体类型,不知道大小,不能为其开辟空间


public Tiger(R r) {
this.r = r;
}

public Tiger(String name, T t, R r, M m) { //构造器使用泛型
this.name = name;
this.t = t;
this.r = r;
this.m = m;
}
//因为静态是和类相关的,在类加载时,对象还没有创建
// static T T1; //错误写法
// 所以,如果静态方法和静态属性使用了泛型,JVM 就无法完成初始化
// public static void f(T t) { //
//
// }

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public T getT() {
return t;
}

public void setT(T t) {
this.t = t;
}

public R getR() {
return r;
}

public void setR(R r) {
this.r = r;
}

public M getM() {
return m;
}

public void setM(M m) {
this.m = m;
}

@Override
public String toString() {
return "Tiger{" +
"name='" + name + '\'' +
", t=" + t +
", r=" + r +
", m=" + m +
'}';
}
}

自定义泛型接口

接口的成员都是静态性质的

image-20220902204040907

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.ep.generic;

/***
* @author dep
* @version 1.0
*/
public class CustomInterfaceGeneric {
public static void main(String[] args) {

}
}
/***
* 泛型接口使用的说明
* 1. 接口中,静态成员也不能使用泛型
* 2. 泛型接口的类型, 在继承接口或者实现接口时确定
* 3. 没有指定类型,默认为 Object
*/
interface IUsb<T,R> {
int n = 10;
//T t; // 不能这样使用

//普通方法中,可以使用接口泛型
R get(T t);

void hi(R r);

void run(R r1, R r2, T u1, T u2);

//在 jdk8 中,可以在接口中,使用默认方法, 也是可以使用泛型
default R method(T t) {
return null;
}

}

//在继承接口 指定泛型接口的类型
interface IA extends IUsb<String ,Double>{

}
//当我们去实现 IA 接口时,因为 IA 在继承 IUsu 接口时,指定了 T 为 String R 为 Double
// ,在实现 IUsu 接口的方法时,使用 String 替换 T, 是 Double 替换 R
class AA implements IA{

@Override
public Double get(String s) {
return null;
}

@Override
public void hi(Double aDouble) {

}

@Override
public void run(Double r1, Double r2, String u1, String u2) {

}
}

//实现接口时,直接指定泛型接口的类型
// 给 T 指定 Integer 给 R 指定了 Float
// 所以,当我们实现 IUsb 方法时,会使用 Integer 替换 T, 使用 Float 替换 R
class BB implements IUsb<Integer,Float>{

@Override
public Float get(Integer integer) {
return null;
}

@Override
public void hi(Float aFloat) {

}

@Override
public void run(Float r1, Float r2, Integer u1, Integer u2) {

}
}
//没有指定类型,默认为 Object
// 建议直接写成 IUsb<Object,Object>
class CC implements IUsb{ //等价 class CC implements IUsb<Object,Object>

@Override
public Object get(Object o) {
return null;
}

@Override
public void hi(Object o) {

}

@Override
public void run(Object r1, Object r2, Object u1, Object u2) {

}
}

自定义泛型方法

image-20220902210332781

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
package com.ep.generic;

/***
* @author dep
* @version 1.0
*/
public class CustomMethodGeneric {
public static void main(String[] args) {
Car car = new Car();
car.fly("保时捷",911); //当调用方法时,传入参数,编译器,就会确定类型
}
}
//泛型方法,可以定义在普通类中, 也可以定义在泛型类中
class Car {
public void run(){ // 普通方法
}
//说明 泛型方法
// 1. <T,R> 就是泛型
// 2. 是提供给 fly 使用的
public<T,R> void fly(T t, R r) {
System.out.println(t.getClass());
System.out.println(r.getClass());
}
}

class Fish<T,R>{ //泛型类
public void run() {//普通方法
}
public<U,M> void eat(U u, M m) {//泛型方法
}
//1. 下面 hi 方法不是泛型方法
// 2. 是 hi 方法使用了类声明的 泛型
public void hi(T t){

}

//泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
public<K> void hello(K k, R r){

}
}

image-20220902212238482

泛型的继承和通配符

image-20220902212443114

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
package com.ep.generic;

import java.util.ArrayList;
import java.util.List;

/***
* @author dep
* @version 1.0
* 泛型的继承和通配符
*/
public class GenericExtends {
public static void main(String[] args) {
//泛型没有继承性
// List<Object> list = new ArrayList<String>(); // 错误写法

//举例说明下面三个方法的使用
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AAA> list3 = new ArrayList<>();
List<BBB> list4 = new ArrayList<>();
List<CCC> list5 = new ArrayList<>();

//如果是 List<?> c ,可以接受任意的泛型类型
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);

//List<? extends AAA> c: 表示 上限,可以接受 AAA 或者 AAA 子类
// printCollection2(list1);//×
// printCollection2(list2);//×
printCollection2(list3);//√
printCollection2(list4);//√
printCollection2(list5);//√

//List<? super AA> c: 支持 AA 类以及 AA 类的父类,不限于直接父类
printCollection3(list1);//√
// printCollection3(list2);//×
printCollection3(list3);//√
// printCollection3(list4);//×
// printCollection3(list5);//×

}
//说明: List<?> 表示 任意的泛型类型都可以接受
public static void printCollection1(List<?> c) {
for (Object object : c) { // 通配符,取出时,就是 Object
System.out.println(object);
}
}
// ? extends AAA 表示 上限,可以接受 AAA 或者 AAA 子类
public static void printCollection2(List<? extends AAA> c) {
for (Object object : c) {
System.out.println(object);
}
}
// ? super 子类类名 AAA:支持 AAA 类以及 AAA 类的父类,不限于直接父类, //规定了泛型的下限
public static void printCollection3(List<? super AAA> c) {
for (Object object : c) {
System.out.println(object);
}
}
}

class AAA{

}
class BBB extends AAA{

}
class CCC extends BBB{

}

Junit单元测试

image-20220902214715367

image-20220902214726009

只需要在方法上加@Test注解 ,需要导Junit的包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.junit.jupiter.api.Test;

/***
* @author dep
* @version 1.0
*/
public class junitTest {
@Test
public void m1() {
System.out.println("m1方法调用");
}
@Test
public void m2() {
System.out.println("m2方法调用");
}
}

多线程基础

程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。

进程:

image-20220902223148915

线程:线程由进程创建的,是进程的一个实体

一个进程可以拥有多个线程

  1. ==单线程==:同一个时刻,只允许执行一个线程
  2. ==多线程==:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
  3. ==并发==:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发。
  4. ==并行==:同一个时刻,多个任务同时执行。多核cpu可以实现并行。并发和并行,

image-20220902223435791

线程基本使用

创建线程的两种方式

image-20220902224256231

线程应用案例 1-继承 Thread 类

image-20220902224321324

image-20220902231039340

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
* 通过继承 Thread 类创建线程
*/
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
//创建 Cat 对象,可以当做线程使用
Cat cat = new Cat();
cat.start(); // 启动线程

/*
(1) public synchronized void start() {
start0();
}
(2) //start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现
//真正实现多线程的效果, 是 start0(), 而不是 run
private native void start0();
*/
// cat.run();//run 方法就是一个普通的方法, 没有真正的启动一个线程,就会把 run 方法执行完毕,才向下执行


// 说明: 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
// 这时 主线程和子线程是交替执
for (int i = 0; i < 8; i++) {
System.out.println("主线程继续执行" + i + Thread.currentThread().getName());//名字 main
Thread.sleep(1000);
}
}
}

class Cat extends Thread{

//1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
// 2. 我们会重写 run 方法,写上自己的业务代码
// 3. run Thread 类 实现了 Runnable 接口的 run 方法
/**
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/

Integer time = 0;
//重写 run 方法,写上自己的业务逻辑
@Override
public void run() {
while (true) {
//该线程每隔 1 秒。在控制台输出 “喵喵, 我是小猫咪”
System.out.println("喵喵, 我是小猫咪" + (++time) + " 线程名=" + Thread.currentThread().getName());

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if( time == 8) { //运行8次线程退出
break;
}
}
}
}

线程应用案例 2-实现 Runnable 接口

image-20220903220833262

image-20220903220847096

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);
thread.start();

// Dog dog1 = new Dog();//实现了 Runnable
// ThreadProxy threadProxy = new ThreadProxy(dog1);
// threadProxy.start();

}
}

//线程代理类 , 模拟了一个极简的 Thread 类
class ThreadProxy implements Runnable {//你可以把 Proxy 类当做 ThreadProxy
private Runnable target = null;//属性,类型是 Runnable
@Override
public void run() {
if (target != null) {
target.run();//动态绑定(运行类型 Tiger)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0();//这个方法时真正实现多线程方法
}
public void start0() {
run();
}

}
class Dog implements Runnable{ //通过实现 Runnable 接口,开发线程
int count = 0;

@Override
public void run() {
while (true) {
System.out.println("hi" + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 10) {
break;
}
}
}
}

image-20220903230812941

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
* main 线程启动两个子线程
*/
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread = new Thread(t1);
Thread thread1 = new Thread(t2);
thread1.start(); //启动第 1 个线程
thread.start(); //启动第 2 个线程
}
}

class T1 implements Runnable {
int count = 0;

@Override
public void run() {
while (true) {
System.out.println("T1正在执行" + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 10) {
break;
}
}
}
}

class T2 implements Runnable {
int count = 0;

@Override
public void run() {
while (true) {
System.out.println("T2正在执行" + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 10) {
break;
}
}
}
}

继承 Thread vs 实现 Runnable 的区别

image-20220903230839801

多线程售票

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class ThreadSellTicket {
public static void main(String[] args) {
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
//
// // //这里我们会出现超卖..
// sellTicket01.start(); //启动售票线程
// sellTicket02.start(); //启动售票线程
// sellTicket03.start(); //启动售票线程

System.out.println("===使用实现接口方式来售票=====");
SellTicket02 sellTicket02 = new SellTicket02();
// 开启三个线程来卖票
new Thread(sellTicket02).start(); //第 1 个线程-窗口
new Thread(sellTicket02).start(); //第 1 个线程-窗口
new Thread(sellTicket02).start(); //第 1 个线程-窗口

}
}
// 第一种继承Thread实现多线程售票
class SellTicket01 extends Thread {

private static int ticketNum = 100; //让多个线程共享 ticketNum

@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}

}
}
// 第二种实现Runnable接口多线程卖票
class SellTicket02 implements Runnable {
private int ticketNum = 100; //让多个线程共享 ticketNum

@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}

}
}

线程终止

image-20220904203315117

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();

Thread.sleep(10*1000); // 休眠10秒钟
t.setLoop(false); // 通知线程停止
}
}

class T extends Thread{
private Integer time = 0;

private Boolean Loop = true;

//重写 run 方法,
@Override
public void run() {
while (Loop) {
//该线程每隔 1 秒。在控制台输出 “喵喵, 我是小猫咪”
System.out.println("T" + (++time) + " 线程名=" + Thread.currentThread().getName());

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

public void setLoop(Boolean loop) {
Loop = loop;
}
}

线程常用方法

image-20220904205629424

image-20220904205643770

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class ThreadMethod01 {
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start();

threadDemo1.setName("自定义线程名"); // 设置线程名
System.out.println(threadDemo1.getName());
threadDemo1.setPriority(Thread.MIN_PRIORITY); // 设置线程优先级
threadDemo1.interrupt(); // 会终止休眠的线程
}
}
class ThreadDemo1 extends Thread{

private int count = 0;

@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行..." + i);
}

try {
System.out.println(Thread.currentThread().getName() + "休眠中...");
Thread.sleep(200000);
} catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
System.out.println("interrupt执行了....");
}
}
}
}

image-20220904213943403

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
ChildThread childThread = new ChildThread();
childThread.start();
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(i == 5) {
// 让childThread插队到主线程前面,这样main就会等待childThread执行完毕,再执行
// 如果没有join那么,childThread和Main线程就会交替执行
// childThread.join(); //立即将子线程,插入到 main 线程,让 子线程先执行
Thread.yield(); // 主线程礼让,但不一定会成功

}
System.out.println("主线程正在执行------" + i);
}

}
}
class ChildThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("子线程正在执行....." + i);
}
}
}

用户线程和守护线程

image-20220904215908185

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
*/
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
DaemonThread daemonThread = new DaemonThread();
// 将daemonThread设置为守护线程,当所有线程结束后,daemonThread也就自动结束
// 如果没有设置,那么即使main线程执行完毕,daemonThread也不退出,
daemonThread.setDaemon(true); // 将该线程设为守护线程
daemonThread.start(); // 开启守护线程
for (int i = 0; i < 10; i++) {
System.out.println("主线程工作中=======" + i);
Thread.sleep(1000);
}
}
}
class DaemonThread extends Thread{

@Override
public void run() {
for(;;) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("守护线程正在执行中....");
}
}
}

线程的生命周期

JDK 中用 Thread.State 枚举表示了线程的几种状态

image-20220904221251751

线程状态转换图

image-20220904221331994

Synchronized

线程同步机制

image-20220904223723228

同步具体方法-Synchronized

image-20220904223745760

image-20220904230615412

互斥锁

image-20220904230635212

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
public class ThreadSellTicket {
public static void main(String[] args) {
System.out.println("===使用实现接口方式来售票=====");
SellTicket03 sellTicket03 = new SellTicket03();
// 开启三个线程来卖票
new Thread(sellTicket03).start(); //第 1 个线程-窗口
new Thread(sellTicket03).start(); //第 2 个线程-窗口
new Thread(sellTicket03).start(); //第 3 个线程-窗口

}
}

// 使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {
private int ticketNum = 100; //让多个线程共享 ticketNum
Object object = new Object();
private Boolean loop = true;

//同步方法(静态的)的锁为当前类本身
// 1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
// 2. 如果在静态方法中,实现一个同步代码块.
// /* synchronized (SellTicket03.class) { System.out.println("m2"); } */
public synchronized static void m1() {
}
public static void m2() {
synchronized (SellTicket03.class) {
System.out.println("m2");
}
}

//1. public synchronized void sell() {} 就是一个同步方法
// 2. 这时锁在 this 对象
// 3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象
public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行 sell 方法
synchronized (/*this*/ object) { //只要保证是同一个对象就行了
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}
}

@Override
public void run() {
while (loop) {
sell();
}

}
}

image-20220904231938809

线程的死锁

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.

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
package com.ep.thread_;

/***
* @author dep
* @version 1.0
* 模拟死锁现象
*/
public class DeadLock {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}

//线程
class DeadLockDemo extends Thread {
static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}

@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + " 进入 1");
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入 3");
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 4");
}
}
}
}
}

释放锁

下面操作会释放锁

image-20220904232559114

下面操作不会释放锁

image-20220904232616429

作业

image-20220905221223250

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
package com.ep.thread_;


import java.util.Scanner;

/***
* @author dep
* @version 1.0
*/
public class HomeWork01 {
public static void main(String[] args) {
Thread001 thread001 = new Thread001();
thread001.start(); // 开启线程1

Thread002 thread002 = new Thread002();
thread002.setT(thread001);
new Thread(thread002).start();
}
}

class Thread001 extends Thread{
private Boolean loop = true;

public void setLoop(Boolean loop) {
this.loop = loop;
}

@Override
public void run() {
while (loop){
System.out.println(Thread.currentThread().getName() + "随机数:" + ((int)Math.random() * 100 + 1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

class Thread002 implements Runnable {

private Thread001 t;

public void setT(Thread001 t) {
this.t = t;
}

@Override
public void run() {
Scanner scanner = new Scanner(System.in);
char input = ' ';
while (input != 'Q') {
input = scanner.next().charAt(0);
if (input == 'Q') {
t.setLoop(false); // 通知线程结束
}
}
}
}

IO 流

文件流

image-20220905225021630

常用的文件操作

创建文件对象相关构造器和方法

image-20220905225055914

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
package com.ep.file;

import org.junit.Test;

import java.io.File;
import java.io.IOException;

/***
* @author dep
* @version 1.0
* 创建文件
*/
public class FileCreate {
public static void main(String[] args) {


}
//方式 1 new File(String pathname)
@Test
public void create01() throws IOException {
String filePath = "e:\\news.txt";
File file = new File(filePath);
file.createNewFile();
}
//方式 2 new File(File parent,String child) //根据父目录文件+子路径构建
@Test
public void create02() throws IOException {
File parentFile = new File("e://");
//这里的 file 对象,在 java 程序中,只是一个对象
// 只有执行了 createNewFile 方法,才会真正的,在磁盘创建该文件
File file = new File(parentFile, "news2.txt");
file.createNewFile();
}
//方式 3 new File(String parent,String child) //根据父目录+子路径构建
@Test
public void create03() throws IOException {
// String parentPath = "e://";
String parentPath = "e:\\";
String fileName = "new4.txt";
File file = new File(parentPath, fileName);
file.createNewFile();
}
}

获取文件的相关信息

image-20220905230306283

image-20220905230328300

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FileInfo {
public static void main(String[] args) {
//先创建文件对象
File file = new File("e:\\news.txt"); //调用相应的方法,得到对应信息
System.out.println("文件名字=" + file.getName());
//getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
System.out.println("文件绝对路径=" + file.getAbsolutePath());
System.out.println("文件父级目录=" + file.getParent());
System.out.println("文件大小(字节)=" + file.length());
System.out.println("文件是否存在=" + file.exists());//T
System.out.println("是不是一个文件=" + file.isFile());//T
System.out.println("是不是一个目录=" + file.isDirectory());//F
}
}

目录的操作和文件删除

image-20220906155706591

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
package com.ep.file;

import org.junit.Test;

import java.io.File;

/***
* @author dep
* @version 1.0
*/
public class MkdirFile {
public static void main(String[] args) {

}
@Test
public void m1() {
File file = new File("e://news.txt");
if(file.exists()) {
file.delete();
}else {
System.out.println("文件不存在");
}
}
@Test
public void m2() {
File file = new File("e://demo");
if(file.exists()) {
if(file.delete()) { // demo目录下不能有文件
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}else {
System.out.println("文件不存在");
}
}
@Test
public void m3() {
File file = new File("e://demo//a//b//c");
if(file.exists()) {
if(file.delete()) {
System.out.println("删除成功");
}else {
System.out.println("删除是啊比");
}
}else {
System.out.println("文件不存在");
file.mkdirs();
}
}
}

IO 流原理及流的分类

image-20220906165446020

image-20220906165457335

流的分类

image-20220906165528482

image-20220906165546145

FileInputStream

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
package com.ep.file;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;

/***
* @author dep
* @version 1.0
* 演示 FileInputStream 的使用(字节输入流 文件--> 程序)
*/
public class FileInputStream_ {
public static void main(String[] args) {

}
/***
* 演示读取文件...
* 单个字节的读取,效率比较低
* -> 使用 read(byte[] b)
* */
@Test
public void readFile01() {
String filePath = "e:\\hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象,用于读取 文件
fileInputStream = new FileInputStream(filePath);
//从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
// 如果返回-1 , 表示读取完毕
while((readData = fileInputStream.read()) != -1){
System.out.print((char) readData); //转成 char 显示
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/***
* 使用 read(byte[] b) 读取文件,
* 提高效率 */
@Test
public void readFile02() {
String filePath = "e:\\hello.txt";
//字节数组
byte[] buf = new byte[8]; //一次读取 8 个字节
int readLength = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象,用于读取 文件
fileInputStream = new FileInputStream(filePath);
//从该输入流读取最多 b.length 字节的数据到字节数组。
// 此方法将阻塞,直到某些输入可用。
// 如果返回-1 , 表示读取完毕
// /如果读取正常, 返回实际读取的字节数
while((readLength = fileInputStream.read(buf)) != -1){
System.out.print(new String(buf,0 ,readLength));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

FileOutputStream

image-20220906182001549

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
package com.ep.file;

import org.junit.Test;

import java.io.FileOutputStream;
import java.io.IOException;

/***
* @author dep
* @version 1.0
*/
public class FileOutputStream_ {
public static void main(String[] args) {

}
/***
* 演示使用 FileOutputStream 将数据写到文件中,
* 如果该文件不存在,则创建该文件
* */
@Test
public void write() {
//创建 FileOutputStream 对象
String filePath = "e:\\a.txt";
FileOutputStream fileOutputStream = null;

try {
//得到 FileOutputStream 对象 对象
//1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
// 2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
fileOutputStream = new FileOutputStream(filePath, true);
//写入一个字节
//fileOutputStream.write('a');
String str = "hello,word";
//str.getBytes() 可以把 字符串-> 字节数组
//fileOutputStream.write(str.getBytes());
/*write(byte[] b, int off, int len) 将 len 字节从位于偏移量 off 的指定字节数组写入此文件输出流 */
fileOutputStream.write(str.getBytes(),0,3);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

文件拷贝

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
package com.ep.file;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/***
* @author dep
* @version 1.0
*/
public class FileCopy {
public static void main(String[] args) {
//1. 创建文件的输入流 , 将文件读入到程序
// 2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件.
String srcFilePath = "e:\\a.png";
String destFilePath = "e:\\a2.png";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;

try {
fileInputStream = new FileInputStream(srcFilePath);
fileOutputStream = new FileOutputStream(destFilePath);
//定义一个字节数组,提高读取效果
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1) {
//读取到后,就写入到文件 通过 fileOutputStream
// 即,是一边读,一边写
fileOutputStream.write(buf,0,readLen); //一定要使用这个方法
}
System.out.println("拷贝结束");

} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

FileReader 和 FileWriter 介绍

image-20220906202848758

FileReader

image-20220906202913646

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
package com.ep.file;

import org.junit.Test;

import java.io.FileReader;
import java.io.IOException;

/***
* @author dep
* @version 1.0
*/
public class FileReader_ {
public static void main(String[] args) {

}
/**
* 单个字符读取文件
*/
@Test
public void readFile01() {
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int data = 0;
//1. 创建 FileReader 对象
try {
fileReader = new FileReader(filePath);
//循环读取 使用 read, 单个字符读取
while ((data = fileReader.read()) != -1) {
System.out.print((char)data);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/*** 字符数组读取文件 */
@Test
public void readFile02() {
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int readLen = 0;
char[] buf = new char[8];
//1. 创建 FileReader 对象
try {
fileReader = new FileReader(filePath);
//循环读取 使用 read(buf), 返回的是实际读取到的字符数
// 如果返回-1, 说明到文件结束
while ((readLen = fileReader.read(buf)) != -1) {
System.out.print(new String(buf,0 , readLen));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

FileWriter

image-20220906205938044

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.ep.file;

import java.io.FileWriter;
import java.io.IOException;

/***
* @author dep
* @version 1.0
*/
public class FileWriter_ {
public static void main(String[] args) {
String filePath = "e:\\note.txt";
//创建 FileWriter 对象
FileWriter fileWriter = null;
char[] chars = {'a', 'b', 'c'};
try {
fileWriter = new FileWriter(filePath); //默认是覆盖写入
// 3) write(int):写入单个字符
fileWriter.write('H');
// 4) write(char[]):写入指定数组
fileWriter.write(chars);
// 5) write(char[],off,len):写入指定数组的指定部分
fileWriter.write("我是张三".toCharArray(), 0, 3);
// 6) write(string):写入整个字符串
fileWriter.write(" 你好北京~");
// 7) write(string,off,len):写入字符串的指定部分
fileWriter.write("天生我材必有用",0, 3);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
// 但一定要关闭,关闭之后才会被写进去
fileWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

节点流和处理流

image-20220906213620787

节点流和处理流一览图

image-20220906213644355

节点流和处理流的区别和联系

image-20220906213717300

处理流的功能主要体现在以下两个方面:

image-20220906214026704

处理流-BufferedReader 和 BufferedWriter

image-20220906214102229

BufferedReader

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
package com.ep.file;

import java.io.BufferedReader;
import java.io.FileReader;

/***
* @author dep
* @version 1.0
* 演示 bufferedReader 使用
*/
public class BuffReader_ {
public static void main(String[] args) throws Exception {
String filePath = "e:\\story.txt";
//创建 bufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));

//读取 String line;
// 按行读取, 效率高
// 说明 //1. bufferedReader.readLine() 是按行读取文件
// 2. 当返回 null 时,表示文件读取完毕
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
//关闭流, 这里注意,只需要关闭 BufferedReader ,因为底层会自动的去关闭 节点流
// FileReader。
/* public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close();//in 就是我们传入的 new FileReader(filePath), 关闭了.
}
finally {
in = null;
cb = null;
}
}
} */
bufferedReader.close();
}
}

BufferedWriter

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
package com.ep.file;

import java.io.BufferedWriter;
import java.io.FileWriter;

/***
* @author dep
* @version 1.0
*/
public class BufferedWritter_ {
public static void main(String[] args) throws Exception {
String filePath = "e://note.txt";
//1. new FileWriter(filePath, true) 表示以追加的方式写入
// 2. new FileWriter(filePath) , 表示以覆盖的方式写入
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath, true));

bufferedWriter.write("hello1, java");
bufferedWriter.newLine(); //插入一个和系统相关的换行
bufferedWriter.write("hello2, java");
bufferedWriter.newLine();
bufferedWriter.write("hello3, java");

//说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭
bufferedWriter.close();
}
}

文件拷贝

BufferedReader 和 BufferedWriter 是按照字符操作

不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏

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.ep.file;

import java.io.*;

/***
* @author dep
* @version 1.0
*/
public class BufferedCopy {
public static void main(String[] args){
//1. BufferedReader 和 BufferedWriter 是按照字符操作
// 2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏
String srcFilePath = "e://a.txt";
String destFilePath = "e://a4.txt";
BufferedWriter bufferedWriter = null;
BufferedReader bufferedReader = null;
String line;
try {
bufferedReader = new BufferedReader(new FileReader(srcFilePath));
bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));

while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
}

} catch (Exception e) {
throw new RuntimeException(e);

} finally {
try {
bufferedWriter.close();
bufferedReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}

}
}
}

处理流-BufferedInputStream 和 BufferedOutputStream

image-20220906222444052

image-20220906222509716

文件拷贝

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
package com.ep.file;

import java.io.*;

/***
* @author dep
* @version 1.0
* 演示使用 BufferedOutputStream 和 BufferedInputStream 使用
* 使用他们,可以完成二进制文件拷贝.
* 思考:字节流可以操作二进制文件,可以操作文本文件吗?当然可以
*/
public class BufferedCopy02 {
public static void main(String[] args){
// String srcFilePath = "e://a.png";
// String destFilePath = "e://a4.png";
String srcFilePath = "e://a.txt";
String destFilePath = "e://a4.txt";
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destFilePath));
byte[] bytes = new byte[1024];
int readLen = 0;
//当返回 -1 时,就表示文件读取完毕
while ((readLen = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, readLen);
}

} catch (Exception e) {
throw new RuntimeException(e);

} finally {
try {
//关闭流 , 关闭外层的处理流即可,底层会去关闭节点流
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}

}
}
}

对象流

image-20220907154853961

image-20220907154909592

ObjectOutputStream 提供 序列化功能

ObjectInputStream 提供 反序列化功能

image-20220907154946311

ObjectOutputStream

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
package com.ep.file;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/***
* @author dep
* @version 1.0
*/
public class ObjectOutputStream_ {
public static void main(String[] args) {
//序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
String filePath = "e:\\data.dat";
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化数据到 e:\data.dat
oos.writeInt(100); // int -> Integer (实现了 Serializable)
oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)
oos.writeChar('a');// char -> Character (实现了 Serializable)
oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
oos.writeUTF("你好啊");//String
//保存一个 dog 对象
Dog dog = new Dog("大黄", 18);
oos.writeObject(dog);

} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
class Dog implements Serializable {
private String name;
private int age;

//serialVersionUID序列化的版本号,可以提高兼容性
private static final long serialVersionUID = 1L;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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

ObjectInputStream

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
package com.ep.file;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/***
* @author dep
* @version 1.0
*/
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
String filePath = "e:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

//1。读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
// 2。否则会出现异常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object object = ois.readObject();
System.out.println(object);
Dog dog = (Dog) object;
System.out.println(dog.getName());

ois.close();

}
}

image-20220907172318556

转换流

image-20220907180946691

InputStreamReader

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
package com.ep.file;

import java.io.*;

/***
* @author dep
* @version 1.0
* 演示使用 InputStreamReader 转换流解决中文乱码问题
* 将字节流 FileInputStream 转成字符流 InputStreamReader, 指定编码 gbk/utf-8
*/
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.txt";
//解读
// 1. 把 FileInputStream 转成 InputStreamReader
//2. 指定编码 gbk
// InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//3. 把 InputStreamReader 传入 BufferedReader
// BufferedReader br = new BufferedReader(isr);

//将 2 和 3 合在一起
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));
//4. 读取
String s = bufferedReader.readLine();
System.out.println(s);

bufferedReader.close();

}
}

OutputStreamReader

1
2
3
4
5
6
7
8
9
10
11
public class OutputStreamReader_ {
public static void main(String[] args) throws IOException {
// 1.创建流对象
OutputStreamWriter oss = new OutputStreamWriter(new FileOutputStream("e://a.txt"), "gbk");
// 2.写入
oss.write("你好,java");
// 3.关闭
oss.close();
}
}

打印流

image-20220907213336042

image-20220907213350515

PrintStream

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
package com.ep.file;

import java.io.IOException;
import java.io.PrintStream;

/***
* @author dep
* @version 1.0
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
//在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
PrintStream out = System.out;
/* public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
*/
out.println("你好啊");
//因为 print 底层使用的是 write , 所以我们可以直接调用 write 进行打印/输出
out.write("你好".getBytes());
//我们可以去修改打印流输出的位置/设备
// 1. 输出修改成到 "e:\\f1.txt"
// 2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt
// 3. public static void setOut(PrintStream out) {
// checkIO();
// setOut0(out);
// native 方法,修改了 out
// }
System.setOut(new PrintStream("e://f1.txt"));
out.println("这段文字会写到文件中");

out.close();

}
}

PrintWriter

1
2
3
4
5
6
7
8
9
10
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
//PrintWriter printWriter = new PrintWriter(System.out);
PrintWriter printWriter = new PrintWriter(new FileWriter("e://f2.txt"));
printWriter.println("hi, 北京你好~~~~");
printWriter.close();
//flush + 关闭流, 才会将数据写入到文件..
}
}

Properties 类

传统方式读取properties文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Properties01 { 
public static void main(String[] args) throws IOException {
//读取 mysql.properties 文件,并得到 ip, user 和 pwd
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while ((line = br.readLine()) != null) { //循环读取
String[] split = line.split("=");
//如果我们要求指定的 ip 值
if("ip".equals(split[0])) {
System.out.println(split[0] + "值是: " + split[1]);
}
}
br.close();
}
}

image-20220907222355816

image-20220907222411230

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
package com.ep.properties_;

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

/***
* @author dep
* @version 1.0
*/
public class properties01 {
public static void main(String[] args) throws IOException {
//使用 Properties 类来读取 mysql.properties 文件
// 1. 创建 Properties 对象
Properties properties = new Properties();
//2. 加载指定配置文件
properties.load(new FileReader("src//main//java//com//ep//properties_//mysql.properties"));
//3. 把 k-v 显示控制台
properties.list(System.out);
//4. 根据 key 获取对应的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println(user);
System.out.println(pwd);
}
}

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
package com.ep.properties_;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

/***
* @author dep
* @version 1.0
*/
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用 Properties 类来创建 配置文件, 修改配置文件内容
Properties properties = new Properties();
//创建
// 1.如果该文件没有 key 就是创建
// 2.如果该文件有 key ,就是修改
properties.setProperty("user","root");
properties.setProperty("pwd","8888");
properties.setProperty("name","张三"); // 汉字会被存储为Unicode

//将 k-v 存储文件中即可
// 第二个参数是注解说明
properties.store(new FileOutputStream("src//main//java//com//ep//properties_//mysql2.properties"), null);
System.out.println("保存配置文件成功~");
}
}

网络编程

InetAddress 类

image-20220908223434308

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class InetAddress_ {
public static void main(String[] args) throws UnknownHostException {
//获取本机 InetAddress 对象 getLocalHost
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost); // SC-201902031211/192.168.247.1

//根据指定主机名/域名获取 ip 地址对象 getByName
InetAddress host = InetAddress.getByName("www.baidu.com");
System.out.println(host); // www.baidu.com/14.215.177.39

//获取 InetAddress 对象的主机名 getHostName
String hostName = host.getHostName();
System.out.println(hostName); // www.baidu.com

//获取 InetAddress 对象的地址 getHostAddress
String hostAddress = host.getHostAddress();
System.out.println(hostAddress); // 14.215.177.39
}
}

Socket

image-20220908230618731

image-20220908230638462

TCP 网络通信编程

image-20220908230713638

应用案例 1(使用字节流)

image-20220908230751940

服务端

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
package com.ep.network_.socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 服务端
*/
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//思路
// 1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");

//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());

// 3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();

//4. IO 读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf,0 , readLen)); //根据读取到的实际长度,显示内容
}

//5.关闭流和 socket
inputStream.close();
socket.close();
serverSocket.close();

}
}

客户端

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
package com.ep.network_.socket;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 客户端,发送 "hello, server" 给服务端
*/
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//思路
// 1. 连接服务端 (ip , 端口)
// 解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
System.out.println("客户端 socket 返回=" + socket.getClass());

//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道
outputStream.write("你好啊,socket".getBytes());

//4. 关闭流对象和 socket, 必须关闭
outputStream.close();
socket.close();
}
}

应用案例 2(使用字节流)

image-20220909222307268

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
package com.ep.network_.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 服务端
*/
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//思路
// 1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");

//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());

// 3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();

//4. IO 读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf,0 , readLen)); //根据读取到的实际长度,显示内容
}

//5. 获取 socket 相关联的输出流
OutputStream outputStream1 = socket.getOutputStream();
outputStream1.write("hello,Client.服务器端已经收到你的消息".getBytes());
// 设置结束标记
socket.shutdownOutput();

//5.关闭流和 socket
inputStream.close();
outputStream1.close();
socket.close();
serverSocket.close();

}
}

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
package com.ep.network_.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 客户端,发送 "hello, server" 给服务端
*/
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//思路
// 1. 连接服务端 (ip , 端口)
// 解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
System.out.println("客户端 socket 返回=" + socket.getClass());

//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道
outputStream.write("你好啊,socket".getBytes());
// 设置结束标记
socket.shutdownOutput();

//4. 获取和 socket 关联的输入流. 读取数据(字节),并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf,0 , readLen)); //根据读取到的实际长度,显示内容
}


//4. 关闭流对象和 socket, 必须关闭
outputStream.close();
inputStream.close();
socket.close();
}
}

应用案例 3(使用字符流)

image-20220909225036748

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
package com.ep.network_.socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 服务端
*/
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//思路
// 1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");

//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());

// 3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();
//4. IO 读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);


//5. 获取 socket 相关联的输出流
OutputStream outputStream1 = socket.getOutputStream();
// 使用字符输出流的方式回复信息
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream1));
bufferedWriter.write("hello,Client.服务器端已经收到你的消息");
bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
bufferedWriter.flush();//注意需要手动的 flush


//5.关闭流和 socket
bufferedWriter.close();
bufferedReader.close();
socket.close();
serverSocket.close();

}
}

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
package com.ep.network_.socket;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 客户端,发送 "hello, server" 给服务端
*/
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//思路
// 1. 连接服务端 (ip , 端口)
// 解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
System.out.println("客户端 socket 返回=" + socket.getClass());

//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
//3. 通过输出流,写入数据到 数据通道
bufferedWriter.write("你好啊,socket");
bufferedWriter.newLine(); //插入一个换行符,表示写入的内容结束, 注意,要求对方使用 readLine()!!!!
bufferedWriter.flush(); // 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

//4. 获取和 socket 关联的输入流. 读取数据(字节),并显示
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);


//4. 关闭流对象和 socket, 必须关闭
bufferedReader.close();
bufferedWriter.close();
outputStream.close();
socket.close();
}
}

网络上传文件

image-20220910225445208

StreamUtils

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
package com.ep.network_.utils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
* 此类用于演示关于流的读写方法
* */
public class StreamUtils {
/**
* 功能:将输入流转换成 byte[]
* @param is
* @return
* @throws Exception
*/
public static byte[] streamToByteArray(InputStream is) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];
int len;
while((len=is.read(b))!=-1){
bos.write(b, 0, len);
}
byte[] array = bos.toByteArray();
bos.close();
return array;
}

/**
* 功能:将 InputStream 转换成 String
* @param is
* @return
* @throws Exception
*/
public static String streamToString(InputStream is) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder= new StringBuilder();
String line;
while((line=reader.readLine())!=null){ //当读取到 null 时,就表示结束
builder.append(line+"\r\n");
}
return builder.toString();
}
}

TCPFileUploadServer

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
package com.ep.network_.upload;


import com.ep.network_.utils.StreamUtils;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 文件上传服务端
*/
public class TCPFileUploadServer {
public static void main(String[] args) throws Exception {
//1. 服务端在本机监听 8888 端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端在 8888 端口监听....");
//2. 等待连接
Socket socket = serverSocket.accept();

//3. 读取客户端发送的数据
// 通过 Socket 得到输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了
String destFilePath = "src\\abc.png";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
bos.write(bytes);
bos.close();

// 向客户端回复 "收到图片"
// 通过 socket 获取到输出流(字符)
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("服务器端收到图片");
bufferedWriter.flush();//把内容刷新到数据通道
socket.shutdownOutput(); //设置写入结束标记

//关闭其他资源
bufferedWriter.close();
bis.close();
socket.close();
serverSocket.close();
}
}

TCPFileUploadClient

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
package com.ep.network_.upload;

import com.ep.network_.utils.StreamUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;

/***
* @author dep
* @version 1.0
* 文件上传客户端
*/
public class TCPFileUploadClient {
public static void main(String[] args) throws Exception {
//客户端连接服务端 8888,得到 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
//创建读取磁盘文件的输入流
String filePath = "e:\\abc.png";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
//bytes 就是 filePath 对应的字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);

//通过 socket 获取到输出流, 将 bytes 数据发送给服务端
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
bis.close();
socket.shutdownOutput();//设置写入数据的结束标记

//=====接收从服务端回复的消息=====
InputStream inputStream = socket.getInputStream();
//使用 StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭相关的流
inputStream.close();
bos.close();
socket.close();
}
}

反射(reflection)

入门

image-20220911184611775

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
package com.ep.reflection_;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/***
* @author dep
* @version 1.0
*/
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1. 使用 Properties 类, 可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src//re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.ep.reflection_.Cat"
String methodName = properties.get("method").toString();//"hi
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + methodName);

//2. 创建对象 , 传统的方法,行不通 =》 反射机制
// new classfullpath();

//3. 使用反射机制解决
// (1) 加载类, 返回 Class 类型的对象 cls
Class cls = Class.forName(classfullpath);

//(2) 通过 cls 得到你加载的类 com.ep.reflection_.Cat 的对象实例
Object o = cls.newInstance();
//(3) 通过 cls 得到你加载的类 com.ep.reflection_.Cat 的 methodName"hi" 的方法对象
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method = cls.getMethod(methodName);
//(4) 通过 method 调用方法: 即通过方法对象来实现调用方法
method.invoke(o); //传统调用方法是 对象.方法() ,
// 反射机制 方法.invoke(对象)

}
}

反射机制

image-20220911194234384

Java 反射机制原理示意图!!!

image-20220911194258026

Java 反射机制可以完成

image-20220911194327042

反射相关的主要类

注意类,重写构造方法,一定要再写一下无参构造方法

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
package com.ep.reflection_;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

/***
* @author dep
* @version 1.0
*/
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1. 使用 Properties 类, 可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src//re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.ep.reflection_.Cat"
String methodName = properties.get("method").toString();//"hi

//3. 使用反射机制解决
// (1) 加载类, 返回 Class 类型的对象 cls
Class cls = Class.forName(classfullpath);

//(2) 通过 cls 得到你加载的类 com.ep.reflection_.Cat 的对象实例
Object o = cls.newInstance();
//(3) 通过 cls 得到你加载的类 com.ep.reflection_.Cat 的 methodName"hi" 的方法对象
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method = cls.getMethod(methodName);
//(4) 通过 method 调用方法: 即通过方法对象来实现调用方法
method.invoke(o); //传统调用方法是 对象.方法() ,
// 反射机制 方法.invoke(对象)

//java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
// 得到 name 字段
// getField 不能得到私有的属性
Field ageField = cls.getField("age");
System.out.println(ageField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)

//java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
Constructor constructor = cls.getConstructor();
System.out.println(constructor); // public com.ep.reflection_.Cat()

Constructor constructor1 = cls.getConstructor(String.class);
System.out.println(constructor1); // public com.ep.reflection_.Cat(java.lang.String)
}
}

反射优点和缺点

image-20220911201147719

反射调用优化-关闭访问检查

1
2
3
4
Class cls = Class.forName("com.ep.Cat"); 
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);//在反射调用方法时,取消访问检查 (优化,可以提高执行速度)

image-20220911201329625

Class 类

基本介绍

image-20220911204425350

image-20220911204451796

常用方法

image-20220911210126792

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
package com.ep.reflection_.class_;

import com.ep.reflection_.Cat;

import java.lang.reflect.Field;

/***
* @author dep
* @version 1.0
* 演示 Class 类的常用方法
*/
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classAllPath = "com.ep.reflection_.Cat";
//1 . 获取到 Car 类 对应的 Class 对象
// <?> 表示不确定的 Java 类型
Class<?> cls = Class.forName(classAllPath);
System.out.println(cls); // 显示 cls 对象, 是哪个类的 Class 对象 class com.ep.reflection_.Cat
System.out.println(cls.getClass()); //输出 cls 运行类型 class java.lang.Class
//3. 得到包名
System.out.println(cls.getPackage().getName()); // com.ep.reflection_
//4. 得到全类名
System.out.println(cls.getName()); // com.ep.reflection_.Cat
//5. 通过 cls 创建对象实例
Cat cat = (Cat) cls.newInstance();
System.out.println(cat); // cat.toString()
//6. 通过反射获取属性
Field nameField = cls.getField("name");
System.out.println(nameField.get(cat));

System.out.println("=======所有的字段属性====");
for (Field field : cls.getFields()) {
System.out.println(field.get(cat));
}


}
}

获取 Class 类对象

image-20220911214819407

image-20220911214830463

image-20220911214915547

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
package com.ep.reflection_.class_;

import com.ep.reflection_.Cat;

/***
* @author dep
* @version 1.0
* 演示得到 Class 对象的各种方式(6)
*/
public class getClass {
public static void main(String[] args) throws ClassNotFoundException {
//1. Class.forName
String classAllPath = "com.ep.reflection_.Cat"; //通过读取配置文件获取
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);

//2. 类名.class , 应用场景: 用于参数传递
Class cls2 = Cat.class;
System.out.println(cls2);

//3. 对象.getClass(), 应用场景,有对象实例
Cat cat = new Cat();
Class cls3 = cat.getClass();
System.out.println(cls3);

//4. 通过类加载器【4种】来获取到类的 Class 对象
//(1)先得到类加载器 car
ClassLoader classLoader = cat.getClass().getClassLoader();

//(2)通过类加载器得到 Class 对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1 , cls2 , cls3 , cls4 其实是同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());

//5. 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到 Class 类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);//int

//6. 基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE; //其它包装类 BOOLEAN, DOUBLE, LONG,BYTE 等待
System.out.println(type1);
System.out.println(integerClass.hashCode());//?
System.out.println(type1.hashCode());//?
}
}

如下类型有 Class 对象

image-20220911215610289

类加载

image-20220911220703521

image-20220911220143867

类加载时机

image-20220911225218778

类加载过程图

image-20220911225241228

类加载各阶段完成任务

image-20220911225305690

加载阶段

image-20220911225332689

连接阶段-验证

image-20220911225355822

连接阶段-准备

image-20220911225420444

连接阶段-解析

image-20220911225446620

Initialization(初始化)

image-20220911225508632

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

java.lang.Class 类

image-20220911225539019

java.lang.reflect.Field 类

image-20220911225552610

java.lang.reflect.Method 类

image-20220911225605001

java.lang.reflect.Constructor 类

image-20220911225615205

通过反射创建对象

image-20220911232312201

测试 1:通过反射创建某类的对象,要求该类中必须有 public 的无参构造

测试 2:通过调用某个特定构造器的方式,实现创建某类的对象

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
package com.ep.reflection_;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/***
* @author dep
* @version 1.0
* 演示通过反射机制创建实例
*/
public class ReflectCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1. 先获取到 User 类的 Class 对象
Class<?> cls = Class.forName("com.ep.reflection_.User");
//2. 通过 public 的无参构造器创建实例
Object o = cls.newInstance();
System.out.println(o);

//3. 通过 public 的有参构造器创建实例
/***
* constructor 对象就是
* public User(String name) {//public 的有参构造器
* this.name = name;
* }
*/
//3.1 先得到对应构造器
Constructor<?> constructor = cls.getConstructor(String.class);
//3.2 创建实例,并传入实参
Object o1 = constructor.newInstance( "李四");
System.out.println(o1);

//4. 通过非 public 的有参构造器创建实例
// 4.1 得到 private 的构造器对象
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
//4.2 创建实例
// 暴破【暴力破解】 , 使用反射可以访问 private 构造器/方法/属性, 反射面前,都是纸老虎
declaredConstructor.setAccessible(true);
Object o2 = declaredConstructor.newInstance(1, "王五");
System.out.println(o2);

}
}
class User { //User 类
private int age = 10;
private String name = "张三";
public User() {//无参 public
}

public User(String name) {//public 的有参构造器
this.name = name;
}

private User(int age, String name) {//private 有参构造器
this.age = age;
this.name = name;
}

public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}

通过反射访问类中的成员

image-20220911233443998

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
package com.ep.reflection_;

import java.lang.reflect.Field;

/***
* @author dep
* @version 1.0
*/
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1. 得到 Student 类对应的 Class 对象
Class<?> studentClass = Class.forName("com.ep.reflection_.Student");
//2. 创建对象
Object o = studentClass.newInstance(); //o 的运行类型就是 Student
System.out.println(o.getClass()); // class com.ep.reflection_.Student

//3. 使用反射得到 age 属性对象
Field ageField = studentClass.getField("age");
//通过反射来操作属性
ageField.set(o,18);
System.out.println(o); // Student [age=18, name=null]
System.out.println(ageField.get(o));//返回 age 属性的值

//4. 使用反射操作 name 属性
Field name = studentClass.getDeclaredField("name");
//对 name 进行暴破, 可以操作 private 属性
name.setAccessible(true);
//name.set(o,"张三");
name.set(null,"张三");//因为 name 是 static 属性,因此 o 也可以写出 null
System.out.println(o);
System.out.println(name.get(o)); //获取属性值
System.out.println(name.get(null)); //获取属性值, 要求 name 是 static

}
}
class Student {//类
public int age;
private static String name;
public Student() {//构造器
}
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}

}

通过反射访问类的方法

image-20220911234315658

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
package com.ep.reflection_;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/***
* @author dep
* @version 1.0
*/
public class ReflecAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1. 得到 Boss 类对应的 Class 对象
Class<?> bossClass = Class.forName("com.ep.reflection_.Boss");
//2. 创建对象
Object o = bossClass.newInstance();
//3. 调用 public 的 hi 方法
// Method hi = bossCls.getMethod("hi", String.class);//OK
//3.1 得到 hi 方法对象
Method hi = bossClass.getMethod("hi", String.class);
//3.2 调用
hi.invoke(o, "张三");

//4. 调用 private static 方法
// 4.1 得到 say 方法对象
Method say = bossClass.getDeclaredMethod("say", int.class, String.class, char.class);
//4.2 因为 say 方法是 private, 所以需要暴破,原理和前面讲的构造器和属性一样
say.setAccessible(true);
System.out.println(say.invoke(o, 1, "李四", '男'));
//4.3 因为 say 方法是 static 的,还可以这样调用 ,可以传入 null
System.out.println(say.invoke(null,2,"张三", '女'));

//5. 在反射中,如果方法有返回值,统一返回 Object , 但是他运行类型和方法定义的返回类型一致
Object reVal = say.invoke(null, 300, "王五", '男');
System.out.println("reVal 的运行类型=" + reVal.getClass());//String

Method m1 = bossClass.getDeclaredMethod("m1");
Object reVal2 = m1.invoke(o);
System.out.println("reVal2 的运行类型=" + reVal2.getClass());//Monster

}
}
class Monster {}

class Boss {//类
public int age;
private static String name;

public Boss() {//构造器
}

public Monster m1() {
return new Monster();
}

private static String say(int n, String s, char c) {//静态方法
return n + " " + s + " " + c;
}

public void hi(String s) {//普通 public 方法
System.out.println("hi " + s);
}
}

正则表达式

入门体验:

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
*/
public class Regexp_ {
public static void main(String[] args) {
String content ="</i><span class=\"refresh-text_1-d1i\">换一换</span></a></div>" +
"</div><div class=\"opr-toplist1-table_3K7iH\"><div>" +
"<div class=\"toplist1-tr_4kE4D\">" +
"<div class=\"toplist1-td_3zMd4 opr-toplist1-link_2YUtD\">" +
"<span class=\"c-index-single toplist1-hot_2RbQT c-color-red toplist1-hot-normal_12THH\" style=\"opacity:1;\"><i class=\"c-icon icon-top_4eWFz\">\uE662</i></span><a target=\"_blank\" title=\"中国探月未来已来\" href=\"/s?wd=%E4%B8%AD%E5%9B%BD%E6%8E%A2%E6%9C%88%E6%9C%AA%E6%9D%A5%E5%B7%B2%E6%9D%A5&amp;rsv_idx=2&amp;tn=baiduhome_pg&amp;usm=6&amp;ie=utf-8&amp;rsv_pq=a0a7c60a000bb8de&amp;oq=1&amp;rsv_t=c46eXOFH6I1ZTI4TBKaqzEMnM2CSghlBdsza4L85Px5VaFjdgenaUxdgvyVpPKy4Apyn&amp;rqid=a0a7c60a000bb8de&amp;rsf=6b09c17bb5c330f4b81b97b89bec9db9_1_15_1&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811\" class=\"c-font-medium c-color-t opr-toplist1-subtitle_3FULy\" data-click=\"{&#39;clk_info&#39;: &#39;index: 1, page: 1&#39;}\">中国探月未来已来</a></div></div><div class=\"toplist1-tr_4kE4D\"><div class=\"toplist1-td_3zMd4 opr-toplist1-link_2YUtD\"><span class=\"c-index-single toplist1-hot_2RbQT c-index-single-hot1\" style=\"opacity:1;\">1</span><a target=\"_blank\" title=\"乌方:乌军已突破至乌俄边境地区\" href=\"/s?wd=%E4%B9%8C%E6%96%B9%EF%BC%9A%E4%B9%8C%E5%86%9B%E5%B7%B2%E7%AA%81%E7%A0%B4%E8%87%B3%E4%B9%8C%E4%BF%84%E8%BE%B9%E5%A2%83%E5%9C%B0%E5%8C%BA&amp;rsv_idx=2&amp;tn=baiduhome_pg&amp;usm=6&amp;ie=utf-8&amp;rsv_pq=a0a7c60a000bb8de&amp;oq=1&amp;rsv_t=c46eXOFH6I1ZTI4TBKaqzEMnM2CSghlBdsza4L85Px5VaFjdgenaUxdgvyVpPKy4Apyn&amp;rqid=a0a7c60a000bb8de&amp;rsf=6b09c17bb5c330f4b81b97b89bec9db9_1_15_2&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811\" class=\"c-font-medium c-color-t opr-toplist1-subtitle_3FULy\" data-click=\"{&#39;clk_info&#39;: &#39;index: 2, page: 1&#39;}\">乌方:乌军已突破至乌俄边境地区</a><span class=\"c-text c-text-hot opr-toplist1-label_3Mevn\">热</span></div></div><div class=\"toplist1-tr_4kE4D\"><div class=\"toplist1-td_3zMd4 opr-toplist1-link_2YUtD\"><span class=\"c-index-single toplist1-hot_2RbQT c-index-single-hot2\" style=\"opacity:1;\">2</span><a target=\"_blank\" title=\"外籍男子骚扰女性被拘7日限期出境\" href=\"/s?wd=%E5%A4%96%E7%B1%8D%E7%94%B7%E5%AD%90%E9%AA%9A%E6%89%B0%E5%A5%B3%E6%80%A7%E8%A2%AB%E6%8B%987%E6%97%A5%E9%99%90%E6%9C%9F%E5%87%BA%E5%A2%83&amp;rsv_idx=2&amp;tn=baiduhome_pg&amp;usm=6&amp;ie=utf-8&amp;rsv_pq=a0a7c60a000bb8de&amp;oq=1&amp;rsv_t=c46eXOFH6I1ZTI4TBKaqzEMnM2CSghlBdsza4L85Px5VaFjdgenaUxdgvyVpPKy4Apyn&amp;rqid=a0a7c60a000bb8de&amp;rsf=6b09c17bb5c330f4b81b97b89bec9db9_1_15_3&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811\" class=\"c-font-medium c-color-t opr-toplist1-subtitle_3FULy\" data-click=\"{&#39;clk_info&#39;: &#39;index: 3, page: 1&#39;}\">外籍男子骚扰女性被拘7日限期出境</a><span class=\"c-text c-text-hot opr-toplist1-label_3Mevn\">热</span></div></div><div class=\"toplist1-tr_4kE4D\"><div class=\"toplist1-td_3zMd4 opr-toplist1-link_2YUtD\"><span class=\"c-index-single toplist1-hot_2RbQT c-index-single-hot3\" style=\"opacity:1;\">3</span><a target=\"_blank\" title=\"消防员为震区老乡手写感谢信\" href=\"/s?wd=%E6%B6%88%E9%98%B2%E5%91%98%E4%B8%BA%E9%9C%87%E5%8C%BA%E8%80%81%E4%B9%A1%E6%89%8B%E5%86%99%E6%84%9F%E8%B0%A2%E4%BF%A1&amp;rsv_idx=2&amp;tn=baiduhome_pg&amp;usm=6&amp;ie=utf-8&amp;rsv_pq=a0a7c60a000bb8de&amp;oq=1&amp;rsv_t=c46eXOFH6I1ZTI4TBKaqzEMnM2CSghlBdsza4L85Px5VaFjdgenaUxdgvyVpPKy4Apyn&amp;rqid=a0a7c60a000bb8de&amp;rsf=6b09c17bb5c330f4b81b97b89bec9db9_1_15_4&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811\" class=\"c-font-medium c-color-t opr-toplist1-subtitle_3FULy\" data-click=\"{&#39;clk_info&#39;: &#39;index: 4, page: 1&#39;}\">消防员为震区老乡手写感谢信</a></div></div><div class=\"toplist1-tr_4kE4D\"><div class=\"toplist1-td_3zMd4 opr-toplist1-link_2YUtD\"><span class=\"c-index-single toplist1-hot_2RbQT toplist1-hot-normal_12THH\" style=\"opacity:1;\">4</span><a target=\"_blank\" title=\"老人赶集卖的鸭被闷死 路人纷纷买下\" href=\"/s?wd=%E8%80%81%E4%BA%BA%E8%B5%B6%E9%9B%86%E5%8D%96%E7%9A%84%E9%B8%AD%E8%A2%AB%E9%97%B7%E6%AD%BB%20%E8%B7%AF%E4%BA%BA%E7%BA%B7%E7%BA%B7%E4%B9%B0%E4%B8%8B&amp;rsv_idx=2&amp;tn=baiduhome_pg&amp;usm=6&amp;ie=utf-8&amp;rsv_pq=a0a7c60a000bb8de&amp;oq=1&amp;rsv_t=e966suftTwhfvF%2B4kx5q4iSVPInibMp2RJIAQrHE1aaQeK8zR51%2BAbjkA%2BhyOju979wr&amp;rqid=a0a7c60a000bb8de&amp;rsf=6b09c17bb5c330f4b81b97b89bec9db9_1_15_5&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811\" class=\"c-font-medium c-color-t opr-toplist1-subtitle_3FULy\" data-click=\"{&#39;clk_info&#39;: &#39;index: 5, page: 1&#39;}\">老人赶集卖的鸭被闷死 路人纷纷买下</a></div>;

//1. 先创建一个 Pattern 对象 , 模式对象, 可以理解成就是一个正则表达式对象
// Pattern pattern = Pattern.compile("[a-zA-Z]+");
// Pattern pattern = Pattern.compile("[0-9]+");
// Pattern pattern = Pattern.compile("([a-zA-Z]+)|([0-9]+)");
Pattern pattern = Pattern.compile("a target=\"_blank\" title=\"(\\S*)\"");
Matcher matcher = pattern.matcher(content);
int i = 0;
while (matcher.find()) {
//System.out.println((++i) + ":" + matcher.group(0));
System.out.println((++i) + ":" + matcher.group(1));
}
}
}

正则表达式底层实现

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 析 java 的正则表达式的底层实现(重要.)
*/
public class RegTheory {
public static void main(String[] args) {
String content = "1998 年 12 月 8 日,第二代 Java 平台的企业版 J2EE 发布。1999 年 6 月,Sun 公司发布了"
+ "第二代 Java 平台(简称为 Java2)的 3 个版本:J2ME(Java2 Micro Edition,Java2 平台的微型"
+ "版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2 平台的"
+ "标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2 平台的企业版),应"
+ "用 3443 于基于 Java 的应用服务器。Java 2 平台的发布,是 Java 发展过程中最重要的一个"
+ "里程碑,标志着 Java 的应用开始普及 9889 ";
//目标:匹配所有四个数字
// 说明
// 1. \\d 表示一个任意的数字
String regStr = "(\\d\\d)(\\d\\d)";
//2. 创建模式对象[即正则表达式对象]
Pattern pattern = Pattern.compile(regStr);
//3. 创建匹配器
// 说明:创建匹配器 matcher, 按照 正则表达式的规则 去匹配 content 字符串
Matcher matcher = pattern.matcher(content);

/**
* matcher.find() 完成的任务 (考虑分组)
* 什么是分组,比如 (\d\d)(\d\d) ,正则表达式中有() 表示分组,第 1 个()表示第 1 组,第 2 个()表示第 2 组...
* 1. 根据指定的规则 ,定位满足规则的子字符串(比如(19)(98))
* 2. 找到后,将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
* 2.1 groups[0] = 0 , 把该子字符串的结束的索引+1 的值记录到 groups[1] = 4
* 2.2 记录 1 组()匹配到的字符串 groups[2] = 0 groups[3] = 2
* 2.3 记录 2 组()匹配到的字符串 groups[4] = 2 groups[5] = 4
* 2.4.如果有更多的分组.....
* 3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值即 35, 即下次执行 find 时,就从 35 开始匹 配
* public String group(int group) {
* if (first < 0)
* throw new IllegalStateException("No match found");
* if (group < 0 || group > groupCount())
* throw new IndexOutOfBoundsException("No group " + group);
* if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
* return null;
* return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
* }
*
* 1. 根据 groups[0]=0 和 groups[1]=2 的记录的位置,从 content 开始截取子字符串返回
* 就是 [0,2) 包含 0 但是不包含索引为 2 的位置
*/
while (matcher.find()) {
//1. 如果正则表达式有() 即分组
// 2. 取出匹配的字符串规则如下
// 3. group(0) 表示匹配到的子字符串
// 4. group(1) 表示匹配到的子字符串的第一组字串
// 5. group(2) 表示匹配到的子字符串的第 2 组字串
System.out.println(matcher.group(0));
System.out.println("第一组: " + matcher.group(1));
System.out.println("第二组: " + matcher.group(2));
}
}

}

正则表达式语法

image-20220913153013163

元字符(Metacharacter)-转义号 \

image-20220913153041291

image-20220913153127333

元字符-字符匹配符

image-20220913153227267

image-20220913153412507

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 字符匹配符 的使用
*/
public class RegExp02 {
public static void main(String[] args) {
String content = "a11c8abc _ABCy @\n";
//String regStr = "[a-z]"; //匹配 a-z 之间任意一个字符
//String regStr = "[A-Z]"; //匹配 A-Z 之间任意一个字符
//String regStr = "abc"; //匹配 abc 字符串[默认区分大小写]
//String regStr = "(?i)abc"; //匹配 abc 字符串[不区分大小写]
//String regStr = "a((?i)b)c"; // b不区分大小写
//String regStr = "[0-9]"; //匹配 0-9 之间任意一个字符
//String regStr = "[^a-z]" ;//匹配 不在 a-z 之间任意一个字符
//String regStr = "[abcd]"; //匹配 在 abcd 中任意一个字符
//String regStr = "[^\\d]";//匹配 不在 0-9 的任意一个字符
//String regStr = "\\D";//匹配 不在 0-9 的任意一个字符
//String regStr = "\\w"; //匹配 大小写英文字母, 数字,下划线
//String regStr = "\\W";//匹配 等价于 [^a-zA-Z0-9_]
//String regStr = "\\s";//\\s 匹配任何空白字符(空格,制表符等)

//\\S 匹配任何非空白字符 ,和\\s 刚好相反
//String regStr = "\\S";
//. 匹配除 \n 之外的所有字符,如果要匹配.本身则需要使用 \\.
String regStr = ".";

//Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE); // 不区分大小写
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}

}
}

元字符-选择匹配符

image-20220913162751497

元字符-限定符

用于指定其前面的字符和组合项连续出现多少次

image-20220913162835981

image-20220913162931819

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 限定符的使用
*/
public class RegExp03 {
public static void main(String[] args) {
String content = "a211111aaaaaahello";
//a{3},1{4},\\d{2}
// String regStr = "a{3}";// 表示匹配 aaa
// String regStr = "1{4}";// 表示匹配 1111
// String regStr = "\\d{2}";// 表示匹配 两位的任意数字字符
// 细节:java 匹配默认贪婪匹配,即尽可能匹配多的
// String regStr = "a{3,4}"; //表示匹配 aaa 或者 aaaa
// String regStr = "1{4,5}"; //表示匹配 1111 或者 11111
// String regStr = "\\d{2,5}"; //匹配 2 位数或者 3,4,5
// String regStr = "1+"; //匹配一个 1 或者多个 1
// String regStr = "\\d+"; //匹配一个数字或者多个数字
//String regStr = "1*"; //匹配 0 个 1 或者多个 1
String regStr = "a1?"; //匹配 a 或者 a1

Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}

}
}

元字符-定位符

image-20220913193538575

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 定位符的使用
*/
public class RegExp04 {
public static void main(String[] args) {
String content = "123-abc";
// 以至少 1 个数字开头,后接任意个小写字母的字符串
// String regStr = "^[0-9][a-z]*";
//以至少 1 个数字开头, 必须以至少一个小写字母结束
//String regStr = "^[0-9]+\\-[a-z]+$";

//表示匹配边界的 abc[这里的边界是指:被匹配的字符串最后, 也可以是空格的子字符串的后面]
//String regStr = "abc\\b";

//和\\b 的含义刚刚相反
String regStr ="abc\\B";

Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到:" + matcher.group(0));
}
}
}

分组

捕获分组

image-20220913214948423

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
package com.ep.regexp;


import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 捕获分组
*/
public class RegExp05 {
public static void main(String[] args) {
String content = "abcd99996665ds45656sd4565s5";

//下面就是非命名分组
// 1. matcher.group(0) 得到匹配到的字符串
// 2. matcher.group(1) 得到匹配到的字符串的第 1 个分组内容
// 3. matcher.group(2) 得到匹配到的字符串的第 2 个分组内容

// String regStr = "(\\d\\d)(\\d\\d)";//匹配 4 个数字的字符串

//命名分组: 即可以给分组取名
String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";

Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到=" + matcher.group(0));
System.out.println("第 1 个分组内容=" + matcher.group(1));
System.out.println("第 1 个分组内容[通过组名]=" + matcher.group("g1"));
System.out.println("第 2 个分组内容=" + matcher.group(2));
System.out.println("第 2 个分组内容[通过组名]=" + matcher.group("g2"));
}
}
}

非捕获分组

image-20220913221458173

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 非捕获分组,不能通过matcher.group(1)来获取,因为是非捕获分组
*/
public class RegExp06 {
public static void main(String[] args) {
String content = "hello 张三教育 jack 张三老师 张三同学 hello 张三学生";
// 找到 张三教育 、张三老师、张三同学 子字符串
// String regStr = "张三教育|张三老师|张三同学";
// String regStr = "张三(?:教育|老师|同学)"; // 等价于 张三教育|张三老师|张三同学
//String regStr = "张三(?=教育|老师|同学)"; // 与?:不同的是这个只输出前面

String regStr = "张三(?!教育|老师|同学)"; // 匹配张三学生中的张三
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到:" + matcher.group(0));
}
}
}

非贪婪匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 非贪婪匹配
*/
public class RegExp07 {
public static void main(String[] args) {
String content ="abc111111";
//String regStr = "\\d+"; // 默认是贪婪匹配,会匹配到111111
String regStr = "\\d+?"; // 非贪婪匹配
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}

应用实例

image-20220913230021904

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 正则表达式应用实例
*/
public class RegExp08 {
public static void main(String[] args) {
String content = "https://www.bilibili.com/account/history?spm_id_from=333.1007.0.0";
// 汉字
// String regStr = "^[\\u0391-\\uffe5]+$";

// 邮政编码 // 要求:1.是 1-9 开头的一个六位数. 比如:123890 // 2. // 3.
// String regStr = "^[1-9]\\d{5}$";

// QQ 号码 // 要求: 是 1-9 开头的一个(5 位数-10 位数) 比如: 12389 , 1345687 , 187698765
//String regStr = "^[1-9]\\d{4,9}$";

// 手机号码 // 要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999
// String regStr = "^1[?:3|4|5|8]\\d{9}$";

// 网址
//1. 先确定 url 的开始部分 https:// |http://
// 2.然后通过 ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
// 3. /video/BV1fh411y7R8?from=sear 匹配(\/[\w-?=&/%.#]*)?
String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(/[\\w-?=&/%.#]*)?$"; //注意:[. ? *]表示匹配就是.本身

Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);

if (matcher.find()) {
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
}

正则表达式三个常用类

pattern类

![image-20220913233752036`](https://trpora-1300527744.cos.ap-chongqing.myqcloud.com/img/image-20220913233752036.png)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ep.regexp;

import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 演示 matches 方法,用于整体匹配, 在验证输入的字符串是否满足条件使用
*/
public class PatternMethod {
public static void main(String[] args) {

String content = "hello abc hello,";
String regStr = "hello.*";
boolean matches = Pattern.matches(regStr, content);
if (matches) {
System.out.println("整体匹配成功");
} else {
System.out.println("匹配不成功");
}

}
}

Matcher类

image-20220913234707488

image-20220913234742655

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* Matcher 类的常用方法
*/
public class MatcherMethod {
public static void main(String[] args) {
String content = "hello edu jack edutom hello smith hello";
String regStr = "hello";

Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);

while (matcher.find()) {
System.out.println("================");
System.out.println(matcher.start());
System.out.println(matcher.end());
System.out.println("找到:" + content.substring(matcher.start(), matcher.end()));
}

//整体匹配方法,常用于,去校验某个字符串是否满足某个规则
System.out.println("整体匹配:" + matcher.matches());

//完成如果 content 有 hello 替换成 你好
String newContent = matcher.replaceAll("你好");
//注意:返回的字符串才是替换后的字符串 原来的 content 不变化
System.out.println(newContent);

}

}

反向引用

image-20220914141942186

image-20220914142011785

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 反向引用
*/
public class RegExp09 {
public static void main(String[] args) {
// String content = "abc1122333444555asd4545455555sdad6699dsd2332";
String content = "12321-333999111";
// 匹配两个连续的相同数字 (\\d)\\1
// String regStr = "(\\d)\\1";

// 匹配5个连续的相同数字 (\\d)\\1{4}
// String regStr = "(\\d)\\1{4}";
// 匹配个位与千位相同,十位与百位相同 (\\d)(\\d)\\2\\1
// String regStr = "(\\d)(\\d)\\2\\1";

//请在字符串中检索商品编号.形式如:12321-333999111这样的号码,
// 要求满足前面是一个五位数,然后一个-号,然后是一个九位数,连续的每三位要相同
String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";


Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到:" + matcher.group(0));
}

}
}

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
package com.ep.regexp;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
* @author dep
* @version 1.0
* 外部反向引用
*/
public class RegExp10 {
public static void main(String[] args) {
String content = "我....我要....学学学学....编程 java!";

//1. 去掉所有的.
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");

//2. 去掉重复的字 我我要学学学学编程 java!
// 思路
// (1) 使用 (.)\\1+
// (2) 使用 反向引用$1 来替换匹配到的内容
// 注意:因为正则表达式变化,所以需要重置 matcher
Pattern pattern1 = Pattern.compile("(.)\\1+"); //分组的捕获内容记录到$1
content = pattern1.matcher(content).replaceAll("$1"); //使用 反向引用$1 来替换匹配到的内容
System.out.println(content);

}
}

String 类中使用正则表达式

替换功能

String 类 public String replaceAll(String regex,String replacement)

判断功能

String 类 public boolean matches(String regex){} **//**使用 Pattern 和 Matcher 类

分割功能

String 类 public String[] split(String regex)

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
package com.ep.regexp;

/***
* @author dep
* @version 1.0
* String 类中使用正则表达式
*/
public class StringReg {
public static void main(String[] args) {
String content = "2000 年 5 月,JDK1.3、JDK1.4 和 J2SE1.3 相继发布," +
"几周后其" + "获得了 Apple 公司 Mac OS X 的工业标准的支持。" +
"2001 年 9 月 24 日,J2EE1.3 发" + "布。" + "2002 年 2 月 26 日,J2SE1.4 发布。" +
"自此 Java 的计算能力有了大幅提升";
//使用正则表达式方式,将 JDK1.3 和 JDK1.4 替换成 JDK
String newContent = content.replaceAll("JDK1.3|JDK1.4", "JDK");
System.out.println(newContent);

//要求 验证一个 手机号, 要求必须是以 138 139 开头的
String content1 = "13888889999";
boolean matches = content1.matches("1(38|39)\\d{8}");
if (matches) {
System.out.println("格式正确");
}else {
System.out.println("格式不正确");
}

//要求按照 # 或者 - 或者 ~ 或者 数字 来分割
content = "hello#abc-jack12smith~北京";
String[] split = content.split("#|-|~|\\d+");
for (String str : split) {
System.out.println(str);
}


}
}


Java基础
http://example.com/2022/09/13/Java/
作者
Deng ErPu
发布于
2022年9月13日
许可协议