HelloWorld案例

1
2
3
4
5
6
7
public class HelloWorld {
public static void main(String[] args){
System.Out.prinln("HelloWorld");
}
}
// 注意:文件名称必须同名 HelloWorld,方法名必须 main,必须写分号
// javac 编译,java 运行(不需要后缀)

关键字class

被 Java 赋予了特定含义的英文单词,关键字都是字母小写开头。

1
2
// 表示定义一个类。创建一个类。
public class HelloWorld {}

字面量

字面量类型 说明 程序中的写法
整数 不带小数的数字 666,-88
小数 带小数的数字 13.14,-5.21
字符 必须使用单引号,有且仅能一个字符 ‘A’,‘0’, ‘我’
字符串 必须使用双引号,内容可有可无 “HelloWorld”,“黑马程序员”
布尔值 布尔值,表示真假,只有两个值:true,false true 、false
空值 一个特殊的值,空值 值是:null

特殊符号

转义字符 名称 说明 示例
\n 换行 光标移到下一行开头 "A\nB"→ A、B 分两行
\t 水平制表符 对齐到8n+1列 "A\tB"→ B出现在第9列
\ 反斜杠 表示\本身 "C:\\dir" → C:\dir
" 双引号 在字符串中嵌入” "say \"hi\""→ say "hi"
' 单引号 在字符常量中嵌入’ '\''→ 单引号字符
\r 回车 光标回到当前行首(不换行) "ABC\rX"→ 输出 XBC

变量

1
2
3
4
5
6
7
8
9
10
// 定义变量
int a = 1, b = 2;
// 不能重复定义,报错
int a = 1;
// 定义的时候不赋值,取默认值
int a; // 0
String s; // null
boolean flag; // 默认值为 false
// 变量在使用前必须赋值,不然报错
System.out.println(a); // 报错

数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo {
public static void main(String[] args) {
// 二进制:以 0b/0B 开头
int bin = 0b1010; // 十进制 10
// 八进制:以 0 开头(仅一个零)
int oct = 012; // 十进制 10
// 十进制:直接写
int dec = 10; // 十进制 10
// 十六进制:以 0x/0X 开头
int hex = 0xA; // 十进制 10
// 验证它们都相等
System.out.println(bin == oct && oct == dec && dec == hex); // true
}
}

基本数据类型

数据类型 关键字 内存占用 取值范围
整数 byte 1 负的2的7次方 ~ 2的7次方-1(-128~127)
short 2 负的2的15次方 ~ 2的15次方-1(-32768~32767)
int 4 负的2的31次方 ~ 2的31次方-1
long 8 负的2的63次方 ~ 2的63次方-1
浮点数 float 4 1.401298e-45 ~ 3.402823e+38
double 8 4.9000000e-324 ~ 1.797693e+308
字符 char 2 0-65535
布尔 boolean 1 true,false
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
public class VariableDemo3{
public static void main(String[] args){
//1.定义byte类型的变量
//数据类型 变量名 = 数据值;
byte a = 10;
System.out.println(a);

//2.定义short类型的变量
short b = 20;
System.out.println(b);

//3.定义int类型的变量
int c = 30;
System.out.println(c);

//4.定义long类型的变量
long d = 123456789123456789L;
System.out.println(d);

//5.定义float类型的变量
float e = 10.1F;
System.out.println(e);

//6.定义double类型的变量
double f = 20.3;
System.out.println(f);

//7.定义char类型的变量
char g = 'a';
System.out.println(g);

//8.定义boolean类型的变量
boolean h = true;
System.out.println(h);

//9.使用 var 关键字,省略变量类型
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder(); // 省略

//10.常量
final double PI = 3.14; // PI是一个常量
}
}

标识符

  • 必须由数字、字母、下划线_、美元符号$组成。
  • 数字不能开头
  • 不能是关键字
  • 区分大小写的

键盘录入

1
2
3
4
5
6
7
8
9
10
11
//导包,其实就是先找到Scanner这个类在哪
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//当程序运行之后,我们在键盘输入的数据就会被变量 i 给接收了
int i = sc.nextInt();
// String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.println(i);
}
}

结构分类

project(项目、工程)

module(模块)

package(包)

class(类)


运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
// 1.算术运算符(+ - * / %)
/* 1.整数相除结果只能得到整除,如果结果想要是小数,必须要有小数参数。
2.小数直接参与运算,得到的结果有可能是不精确的。*/
System.out.println( 10 / 3); //3
System.out.println(10.0 / 3); //3.3333333333333335

// 溢出不会出错,得到一个奇怪的结果
int sum = 2147483640 + 15; // -2147483641

// % 可以利用取模来判断一个数是奇数还是偶数
System.out.println(15 % 2); //1 奇数


// 2.隐式转换
// 就是把一个取值范围小的数据或者变量,赋值给另一个取值范围大的变量。此时不需要我们额外写代码单独实现,是程序自动帮我们完成的。
/*
取值范围小的,和取值范围大的进行运算,小的会先提升为大的,再进行运算。
byte、short、char三种类型的数据在运算的时候,都会直接先提升为 int,然后再进行运算。
取值范围从小到大的关系:byte short int long float double
*/
double d = 10;
System.out.println(d);//10.0

// 3.显示转换
// 如果要把一个取值范围大的数据或者变量赋值给另一个取值范围小的变量,需要加入强制转换。
// 书写格式:目标数据类型 变量名 = (目标数据类型)被强转的数据;
public class OperatorDemo2 {
public static void main(String[] args) {
double a = 12.3;
int b = (int) a;
System.out.println(b);//12
}
}

// 字符串 +
// 当 + 操作中出现字符串时,此时就是字符串的连接符,会将前后的数据进行拼接,并产生一个新的字符串。
// 当连续进行 + 操作时,从左到右逐个执行的。
1 + "abc" + 1 // "1abc1"
1 + 2 + "abc" + 2 + 1 // "3abc21"

// 字符 +
// 当 + 操作中出现了字符,会拿着字符到计算机内置的ASCII码表中去查对应的数字,然后再进行计算。
char c = 'a';
int result = c + 0;
System.out.println(result); // 97

// 4.自增自减运算符
/*
++ 自增运算符
-- 自减运算符
*/
int a = 0;
System.out.println(++a); // 1
System.out.println(a++); // 1

// 5.赋值运算符 =
// 扩展赋值运算符 +=、-=、*=、/=、%=
// 注意:扩展的赋值运算符中隐层还包含了一个强制转换。
byte a = 10;
byte b = 20;
// a += b;
a = (byte)(a + b)

// 6.关系运算符
// ==、!=、>、>=、<、<=,没有 ===
// 注意:char 也会隐式转换
char a = '1';
int b = 49;
System.out.println(a == b);

// 7.逻辑运算符
// &、|、^、!
System.out.println(true & true);// true 两边都是真,结果才是真。
System.out.println(true | false);//true 两边都是假,结果才是假,如果有一个为真,那么结果就是真。
System.out.println(true ^ true); //false 左右不相同,结果才是 true,左右相同结果就是 false
System.out.println(!false);//true 取反

// 8.短路逻辑运算符 && ||
// && 和 & 、||和|的运行结果都是一模一样的。
// 当左边不能确定整个表达式的结果,右边才会执行​,提高程序的运行效率。

// 9.三元运算符
// 关系表达式 ? 表达式1 :表达式2 ;

原码、反码、补码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 【原码】最高位符号位(0正1负),其余为绝对值
// +5 → 0000 0101
// -5 → 1000 0101
// 缺点:零有两种表示(+0/-0),加减法不能统一运算

// 【反码】正数不变;负数=符号位不变,数值位取反
// +5 → 0000 0101
// -5 → 1111 1010
// 缺点:零仍有两种表示,仅是过渡方案

// 【补码】计算机实际存储方式
// 正数:补码 = 原码
// 负数:补码 = 反码 + 1
// +5 → 0000 0101
// -5 → 1111 1011
// -1 → 1111 1111
// -128→ 1000 0000 (无原码/反码,补码独有)
// 优点:零唯一、加减统一、符号位直接参与运算

输出printf

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
// System.out.printf(String format, Object... args)
// - format: 格式字符串,包含普通文本和 % 占位符
// - args: 可变参数,按顺序与占位符一一匹配(数量必须相等)
// - 返回值: PrintStream 对象本身(支持链式调用)
// - 不会自动换行,需手动使用 %n

// %d 整数 | %f 浮点 | %s 字符串 | %c 字符 | %n 换行 | %% 百分号
int num = 42;
double pi = 3.1415926;
String name = "Java";

System.out.printf("%d | %.2f | %s%n", num, pi, name);
// 42 | 3.14 | Java
// 参数绑定只看 % 的出现顺序,| 只是原样输出的普通文本,不是分隔符

// ==================== 精度控制 (.n) ====================
System.out.printf("%.2f%n", pi); // 3.14 四舍五入保留2位小数
System.out.printf("%.0f%n", pi); // 3 不保留小数
System.out.printf("%.3s%n", name); // Jav 字符串截取前3位

// ==================== 宽度与对齐 ====================
System.out.printf("|%8d|%n", num); // | 42| 默认右对齐补空格
System.out.printf("|%-8d|%n", num); // |42 | 负号左对齐
System.out.printf("|%08d|%n", num); // |00000042| 零填充
// | 仅为视觉边界标记,删除不影响格式化逻辑

// ==================== 标志组合实战 ====================
double price = 12900000;
System.out.printf("金额: %,10.2f%n", price); // 金额: 12,900,000.00
// %,10.2f 拆解:
// , → 千分位分隔符
// 10 → 最小宽度10字符,不足左侧补空格(本例14字符已超出,无需补)
// .2 → 保留2位小数,四舍五入
// f → 浮点类型

流程控制语句

  • 顺序结构

顺序结构没有特定的语法结构,按照代码的先后顺序,依次执行。

  • 判断和选择结构
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
// if 语句
if (关系表达式) {
语句体;
} else if(关系表达式) {
语句体;
} else {
语句体;
}
// 注意:不像 js 中 if 里面不能直接写其他的,比如 int a = 1; if(a){} 会报错
// 但是可以直接写 boolean 值 boolean flag = true; if(flag) {}


// switch 语句
switch (表达式) {
case 1:
语句体1;
break;
case 2:
语句体2;
break;
...
default:
语句体n+1;
break;
}

// JDK12 新特性省略break;
int number = 10;
switch (number) {
case 1 -> System.out.println("一");
case 2 -> System.out.println("二");
case 3 -> System.out.println("三");
case 4 -> {
System.out.println("三");
System.out.println("多行");
}
default -> System.out.println("其他");
}
// 利用case穿透简化代码
// case 穿透,不写 break 会引发 case 穿透现象
/*
switch(week) {
case 1:
case 2:
System.out.println("1,2");
break;
default:
System.out.println("default");
break;
}
*/

// switch 语句还可以匹配字符串。字符串匹配时,是比较内容相等
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("Selected apple");
break;
default:
System.out.println("No fruit selected");
break;
}

// switch,从Java 14开始,逗号隔开,不需要 break,还可以直接返回值。多行 yield 返回值。
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // switch语句返回值
}
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}

循环结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// for 循环
for (初始化语句;条件判断语句;条件控制语句) {
循环体语句;
}

// while 循环
初始化语句;
while(条件判断语句){
循环体;
条件控制语句;
}

// do...while循环
初始化语句;
do{
循环体;
条件控制语句;
}while(条件判断语句);

循环高级

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
// 1.无限循环
for(;;){
System.out.println("循环执行一直在打印内容");
}
while(true){
System.out.println("循环执行一直在打印内容");
}
do{
System.out.println("循环执行一直在打印内容");
}while(true);

// for each 循环
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}

// 2.条件控制语句
break
continue

// 3.Random
import java.util.Random;

public class RandomDemo1 {
public static void main(String[] args) {
//2.创建对象
Random r = new Random();
//3.生成随机数
int number = r.nextInt(100);//包左不包右,包头不包尾
//0 ~ 99
System.out.println(number);
}
}

// 随机数生成秘诀 5-25
// 1.让这个范围的头尾都减去一个值,从零开始 0-20
// 2.尾巴加 1(21)
// 3.最后结果,加上第一步的值
int number = r.nextInt(21) + 5

数组

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
// 数组静态定义
int[] array = new int[]{1, 2, 3};
int arrry[] = new int[]{1, 2, 3};
// 简写
int[] arr = {1, 2, 3}

System.out.println(array); // 地址值
System.out.println(array[0]);
System.out.println(array.length);
array[0] = 0;

// 数组的动态初始化
int[] agesArr = new int[3];
// 初始值(整数类型:0;小数类型:0.0;布尔类型:false;字符类型:'\u0000' 空格;引用类型:null)

// 当访问了数组中不存在的索引,就会引发索引越界异常。
int[] arr = {1,2,3};
System.out.println(arr[3]); // ArrayIndexOutOfBoundsException
System.out.print(); // 打印会在同一行展示

// 使用 for each 循环,但无法获取索引
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}

// 交换数组数据,可以同时定义两个值
//1.定义数组存储数据
int[] arr = {1,2,3,4,5};
//2.利用循环去交换数据
for(int i = 0,j = arr.length - 1; i < j; i++,j--){
//交换变量i和变量j指向的元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//当循环结束之后,那么数组中的数据就实现了头尾交换
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}

// 快速打印数组
import java.util.Arrays;
System.out.println(Arrays.toString(ns));
System.out.println(Arrays.deepToString(ns));

// 二维数组
int[][] arr = new int[2][]; // 创内层数组长度可动态指定(不规则数组)
int[] arr1 = {1,2};
int[] arr2 = {3,4};
arr[0] = arr1;
arr[1] = arr2;
// 不推荐写法,将 arr[0] 指向了新的长度为 2 的数组(地址),覆盖了初始。
int[][] arr = new int[1][1];
System.out.println(Arrays.deepToString(arr));
arr[0] = new int[]{1, 2};
System.out.println(Arrays.deepToString(arr));

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static 数据类型 方法名 ( 参数 ) { 
return 数据 ;
}

public class Demo {
public static void main(String[] args) {
// 静态方法里面只能调用其他静态方法
test(); // 直接调用
}

public static void test() {
System.out.println("test");
}
}

// 方法重载
// 多个方法在同一个类中,多个方法具有相同的方法名,多个方法的参数不相同,类型不同或者数量不同(与返回值无关)。
// 不是重载,只是形参的名称不同,顺序不同:test(double, int) vs test(int, double)
public static void test(double r, double h) {} // 签名: test(double, double)
public static void test(double h, double r) {} // 签名: test(double, double) ← 完全相同!

面向对象

类的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
1.定义类
2.编写类的成员变量
3.编写类的成员方法
*/

public class 类名 {
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
…
// 成员方法
方法1;
方法2;
}

封装

对象代表什么,就得封装对应的数据,并提供数据对应的行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* private 关键字
被 private 修饰的成员,只能在本类进行访问,针对 private 修饰的成员变量,如果需要被其他类使用,提供相应的操作
提供 get变量名() 方法,用于获取成员变量的值,方法用 public修饰
提供 set变量名(参数) 方法,用于设置成员变量的值,方法用 public 修饰
*/
class Student {
private int age;
//提供get/set方法
public void setAge(int a) {
age = a;
}
public int getAge() {
return age;
}
}

this关键字

this 修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)

方法的形参如果与成员变量同名,不带 this 修饰的变量指的是形参,而不是成员变量

方法的形参没有与成员变量同名,不带 this 修饰的变量指的是成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Student {
private String name;
private int age;

public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
}

构造方法

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
/*
1.方法名和类名相同,大小写一致
2.没有返回值类型,连 void 也没有
3.没有具体的返回值
*/
class Student {
private String name;
private int age;

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

public void show() {
System.out.println(name + "," + age);
}
}

/*
如果没有定义构造方法,系统将给出一个默认的无参数构造方法。如果定义了构造方法,系统将不再提供默认的构造方法
重载:如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
无论是否使用,都手工书写无参数构造方法
*/

可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
// 可变参数用类型...定义,可变参数相当于数组类型:
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames(); // 传入0个String
// 变参数改写为String[]类型,调用方需要自己先构造String[]。
// 可变参数可以保证无法传入null,传入0个参数时实际值是一个空数组。

标准类制作(JavaBean)

💡
1.类名需要见名知意
2.成员变量使用 private 修饰
3.提供至少两个构造方法(无参构造方法、带全部参数的构造方法)
4.get和set方法:提供每一个成员变量对应的 setXxx()/getXxx()
5.如果还有其他行为,也需要写上


键盘录入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Scanner sc = new Scanner(System.in);
int i = sc.nextInt(); //只能录入整数
double d = sc.nextDouble(); //录入的整数,小数都会看做小数,录入字母会报错
String s = sc.next(); //录入的所有数据都会看做是字符串
String s1 = sc.nextLine(); //录入的所有数据都会看做是字符串

// next、nextInt、nextDouble 会遇到空格,回车,制表符其中一个就会停止接收数据。
// nextLine 方法是把一整行全部接收完毕。
String s1 = sc.next();
String s2 = sc.next();
System.out.println(s1);
System.out.println(s2);
// 录入一次 a b (注意 a 和 b 之间用空格隔开)
// 那么第一个 next 会接收 a,a 后面是空格,那么就停止,所以打印 s1 是a,
// 但是空格 +b 还在内存中,第二个 next 会去掉前面的空格,只接收 b,所以第二个 s2 打印出来是 b。

// 混用引起的后果
int i = sc.nextInt();
String s = sc.nextLine();
// 当代码运行到第二行,会让我们键盘录入,此时录入 123。
// 实际上我们录的是 123+ 回车。nextInt 是遇到空格,回车,制表符都会停止。
// nextInt只能接受 123,回车还在内存中没有被接收。此时就被 nextLine 接收了。

String

String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!

  • 字符串不可变,它们的值在创建后不能被更改
  • 虽然 String 的值是不可变的,但是它们可以被共享
  • 字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
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
public class StringDemo01 {
String str; // 默认值是 null 而非 ""
public static void main(String[] args) {
//public String():创建一个空白字符串对象,不含有任何内容
String s1 = new String();
System.out.println("s1:" + s1);

//public String(char[] chs):根据字符数组的内容,来创建字符串对象
char[] chs = {'a', 'b', 'c'};
String s2 = new String(chs);
System.out.println("s2:" + s2);

//public String(byte[] bys):根据字节数组的内容,来创建字符串对象
byte[] bys = {97, 98, 99};
String s3 = new String(bys);
System.out.println("s3:" + s3);

//String s = "abc"; 直接赋值的方式创建字符串对象,内容就是abc
String s4 = "abc";
System.out.println("s4:" + s4);

// Java 13开始,字符串可以用"""..."""表示多行字符串
String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
}
}

/* 存储区别
1.通过构造方法创建:通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同
2.直接赋值方式创建:以""方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护
*/

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// == 号的作用(比较基本数据类型:比较的是具体的值,比较引用数据类型:比较的是对象地址值)
// equals 比较值、equalsIgnoreCase 忽略大小写比较
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);

//直接赋值的方式得到对象
String s3 = "abc";
String s4 = "abc";

//比较字符串对象地址是否相同
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // true

//比较字符串内容是否相同
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
System.out.println(s3.equals(s4)); // true
// 注意:键盘输入的字符串和基本数据类型用 == 比较是 false,开发只会用 equals。

//charAt():会根据索引获取对应的字符
//length(): 会返回字符串的长度,注意这个是方法
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
System.out.println(c);
}

// substring 字符串截取
// substring(beginIndex) → 从 beginIndex 到末尾
// substring(beginIndex, endIndex) → [beginIndex, endIndex) 左闭右开

// replace(CharSequence, CharSequence)
// 纯文本替换(非正则),替换所有匹配项
"aaa".replace("a", "b"); // "bbb"

// replaceAll(String regex, String replacement)
// 正则替换所有匹配项
"foo123bar456".replaceAll("\\d+", "#"); // "foo#bar#"
"a$b".replaceAll("\\$", Matcher.quoteReplacement("$")); // 安全替换 $

// replaceFirst(String regex, String replacement)
// 正则仅替换第一个匹配项
"aaa".replaceFirst("a", "b"); // "baa"

StringBuilder

StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// StringBuilder: 可变字符序列,线程不安全,字符串拼接首选
new StringBuilder() // 默认容量16,不够会自动扩容
new StringBuilder(256) // 预分配容量,避免频繁扩容
new StringBuilder("init") // 以指定字符串为初始内容

sb.append("str") // 末尾追加字符串
sb.insert(2, "ab") // 在索引2处插入"ab",原索引2及之后字符后移
sb.delete(1, 3) // 删除索引[1,3)的字符,即索引1和2,不含3
sb.deleteCharAt(1) // 仅删除索引1处的单个字符
sb.replace(1, 3, "xy") // 将索引[1,3)的字符替换为"xy",长度可不同
sb.reverse() // 原地反转所有字符
sb.setCharAt(0, 'A') // 将索引0处的字符改为'A'
sb.length() // 返回当前字符个数(非容量)
sb.charAt(0) // 返回索引0处的字符
sb.indexOf("ab") // 返回子串"ab"首次出现的起始索引,未找到返回-1
sb.substring(1, 3) // 返回索引[1,3)的新String,不修改原sb
sb.toString() // 生成不可变String副本

// equals()比较的是对象引用而非内容,内容比较请用toString().equals()
// 所有涉及范围的方法均遵循[include, exclude)左闭右开约定

StringJoiner

  • StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的。
  • 作用:提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人用。
  • JDK8出现的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// StringJoiner:专门用于拼接带分隔符、前缀、后缀的字符串

// 1. 创建(三种构造)
new StringJoiner(",") // 仅分隔符
new StringJoiner(",", "[", "]") // 分隔符 + 前缀 + 后缀
new StringJoiner(delimiter, prefix, suffix)

// 2. 添加元素(返回自身,支持链式调用)
add("a").add("b").add("c")

// 3. 合并另一个StringJoiner
merge(otherJoiner)

// 4. 获取结果
toString() // 返回拼接后的完整字符串
length() // 返回拼接后字符串的长度

// 5. 空值处理
setEmptyValue("N/A") // 当没有add任何元素时,toString()返回该值而非空串

// 典型输出示例:[a,b,c] 或 (1,2,3)

ArrayList

  • 提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
  • 长度可以变化,只能存储引用数据类型。
  • 泛型:用于约束集合中存储元素的数据类型
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
// 【创建】(始终指定泛型),后面的泛型可以不写
ArrayList<String> list = new ArrayList<>(); // 默认容量10
ArrayList<Integer> nums = new ArrayList<>(100); // 指定初始容量

// 【增】
boolean b1 = list.add("A"); // 尾部添加 O(1),始终返回true
void list.add(1, "B"); // 指定索引插入 O(n),无返回值
boolean b2 = list.addAll(otherList); // 批量添加,集合有变化返回true

// 【删】
boolean b3 = list.remove("A"); // 按值删除 O(n),存在并删除返回true,否则false
String old2 = list.remove(1); // 按索引删除 O(n),返回被删除的元素
boolean b4 = list.removeAll(c); // 批量删除,集合有变化返回true
void list.clear(); // 清空,无返回值

// 【改】
String old3 = list.set(1, "C"); // 替换指定位置 O(1),返回被替换的旧元素

// 【查】
String val = list.get(1); // 按索引获取 O(1),返回指定位置元素
int idx = list.indexOf("A"); // 查找元素索引 O(n),不存在返回-1
int lidx = list.lastIndexOf("A"); // 末次出现索引 O(n),不存在返回-1
boolean b5 = list.contains("A"); // 是否包含 O(n),包含返回true
int size = list.size(); // 当前元素个数,返回int
boolean b6 = list.isEmpty(); // 是否为空,空返回true

// 【遍历】
for (String s : list) { } // 增强for(推荐),无返回值
list.forEach(s -> System.out.println(s)); // Lambda遍历,无返回值(void)

// 【转换】
Object[] arr1 = list.toArray(); // 返回 Object[]
String[] arr2 = list.toArray(new String[0]); // 返回指定类型数组
List<String> sub = list.subList(1, 3); // 返回视图[1,3),非独立副本

// 易错提醒:
// add(index, element) → void(唯一没有返回值的add重载)
// remove(int) → E(返回被删元素) vs remove(Object) → boolean(返回是否删除成功)

基本类型转包装类

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
// byte → Byte | short → Short | int → Integer | long → Long
// float → Float | double → Double | char → Character | boolean → Boolean

// 【自动装箱】基本类型 → 包装类,编译器自动调用 valueOf()
Integer a = 100; // 等价于 Integer.valueOf(100)
Long b = 200L; // 等价于 Long.valueOf(200L)
Boolean c = true; // 等价于 Boolean.valueOf(true)

// 【自动拆箱】包装类 → 基本类型,编译器自动调用 xxxValue()
int x = a; // 等价于 a.intValue()
long y = b; // 等价于 b.longValue()
boolean z = c; // 等价于 c.booleanValue()

// 【String ↔ 基本类型/包装类】
Integer n1 = Integer.valueOf("123"); // String → Integer
int n2 = Integer.parseInt("123"); // String → int(推荐,无装箱开销)
long n3 = Long.parseLong("456"); // String → long
double n4 = Double.parseDouble("3.14"); // String → double
boolean n5 = Boolean.parseBoolean("true"); // String → boolean
String s1 = String.valueOf(n1); // Integer → String(null安全)
String s2 = Integer.toString(123); // int → String
String s3 = n1.toString(); // Integer → String(n1为null时NPE)

// 常见陷阱:
// 1. == 比较包装类只比引用,Integer缓存[-128,127]内==才可靠,超出用equals()
// 2. null拆箱必NPE:Integer num = null; int x = num; → NullPointerException
// 3. 循环中避免频繁自动装箱拆箱,有性能损耗,优先用基本类型

static关键字

1
2
3
4
5
6
7
8
9
10
11
class Foo {
static int count = 0;
public static void main(String[] arg) {}
}

Foo foo = new Foo();
System.out.println(foo.count); // 实例调用,不推荐
System.out.println(Foo.count); // 类调用,推荐写法

// 静态方法只能访问静态变量和方法,非静态的方法都可以访问,静态方法没 this。
// 工具类,构造函数是 private,方法都是静态的,不能创建具体实例。

继承

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
// 通过 extends 关键字
class 父类 { ... }
class 子类 extends 父类 { ... }
// Java是单继承的,一个类只能继承一个直接父类。
// 每个类顶层都是继承于 Object。
// 子类不能访问父类私有方法。

/* 子类不能继承的内容
1.子类不能继承父类的构造方法。
2.值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
*/

/*成员变量
变量访问是就行原则,xxx; this.xxx; super.xxx
*/

/*成员方法
如果子类父类中出现重名的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。
重写:声明不变,重新实现
1.方法名和参数列表都相同
2.必须要保证权限大于等于父类权限
3.返回值类型必须小于等于父类
*/
class Fu {
public void show() {}
public Number getNumber() { return 0; }
}
class Zi extends Fu {
@Override
public void show() { super.show(); }
public Integer getNumber() { return 100; } // 返回 Integer,它是 Number 的子类
}

/*构造方法
子类是无法继承父类构造方法
子类的初始化过程中,必须先执行父类的初始化动作 super(),不写也会存在,且必须第一行
*/
class Person {
public Person() {}
}

class Student extends Person {
public Student() {
super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
}
}

/* this()
默认是去找本类中的其他构造方法,根据参数来确定具体调用哪一个构造方法。
*/
class Student{
private String name ;
public Student() {
this("徐干");
}
public Student(String name) {
this.name = name ;
}
}

多态

是指同一行为,具有多个不同表现形式

前提

  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
46
47
/* 表现形式
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
*/

class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void show() {
System.out.println(name);
}
}

class Student extends Person {
public Student(String name) {
super(name);
}
@Override
public void show() {
System.out.println("学生:" + getName());
}
}

class Teacher extends Person {
public Teacher(String name) {
super(name);
}
@Override
public void show() {
System.out.println("老师:" + getName());
}
}

public class Test {
public static void main(String[] args) {
register(new Student("张三"));
register(new Teacher("王建国"));
}
public static void register(Person p) {
p.show();
}
}

多态特点

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
/*
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
*/
Fu f = new Zi();
// 编译看左边的父类中有没有 name 这个属性,没有就报错
// 在实际运行的时候,把父类 name 属性的值打印出来
System.out.println(f.name);
// 编译看左边的父类中有没有 show 这个方法,没有就报错
// 在实际运行的时候,运行的是子类中的 show 方法
f.show();

/*
多态弊端:子类有些独有的功能,多态的写法就无法访问子类独有功能了。
*/
class Animal{
public void eat(){
System.out.println("动物吃东西!");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
class Test{
public static void main(String[] args){
Animal a = new Cat();
a.eat();
a.catchMouse(); //编译报错,编译看左边,Animal没有这个方法
}
}

/*类型转化
自动转换: 范围小的赋值给范围大的,自动完成:double d = 5;
强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
*/
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat();
// 向下转型
Cat c = (Cat)a;
c.catchMouse();
}
}

// instanceof 关键字
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
// JDK14 新特性
// 先判断 a 是否为 Dog 类型,如果是,则强转成 Dog 类型,转换之后变量名为 d
// 如果不是,则不强转,结果直接是 false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}

包

包在操作系统中其实就是一个文件夹。

  • 包名一般是公司域名的倒写。例如:www.xxx.com 包名就可以定义成 com.xxx.技术名称。
  • 包名必须用 . 连接。
  • 包名的每个路径名必须是一个合法的标识符,而且不能是 Java 的关键字。

导包规则

  • 使用同一个包中的类时,不需要导包。
  • 使用 java.lang 包中的类时,不需要导包。
  • 其他情况都需要导包。
  • 如果同时使用两个包中的同名类,需要用全类名

final

不可改变,最终的含义。可以用于修饰类、方法和变量。

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,有且仅能被赋值一次。

权限修饰符

💡 修饰符

  1. private

可见范围:只能在本类中访问

能修饰:变量 √ 方法 √ 内部类 √ 顶级类 ×

  1. 默认(不写)

可见范围:本类 + 同一个包内可以访问

能修饰:变量 √ 方法 √ 内部类 √ 顶级类 √

  1. protected

可见范围:本类 + 同一个包 + 不同包的子类可以访问

能修饰:变量 √ 方法 √ 内部类 √ 顶级类 ×

  1. public

可见范围:任何地方都可以访问

能修饰:变量 √ 方法 √ 内部类 √ 顶级类 √


代码块

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
// 1. 局部代码块:定义在方法内部,用于控制变量的作用域,及时释放内存。
public void method() {
{
int temp = 10; // temp 仅在此大括号内有效,出块即销毁
}
// System.out.println(temp); // 编译报错,temp 已不可访问
}

// 2. 构造代码块:定义在类中、方法外,每次创建对象时自动执行,用于提取多个构造方法的公共逻辑。
class Student {
{
System.out.println("构造代码块执行"); // 每次 new 对象时都会执行
}
public Student() {
System.out.println("无参构造执行");
}
}
// new Student(); → 输出:构造代码块执行 → 无参构造执行

// 3. 静态代码块:用 static 修饰,类加载时只执行一次,用于初始化静态变量或加载资源。
class Config {
static int maxCount;
static {
maxCount = 100; // 类加载时执行一次,为静态变量赋初始值
System.out.println("静态代码块执行");
}
}
// new Config(); new Config(); → "静态代码块执行" 只打印一次

抽象类

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
// 1. 语法:用 abstract 修饰类,类中可以包含抽象方法(无方法体)
abstract class Animal {
String name;
// 抽象方法:只有声明,没有实现,强制子类去实现
abstract void eat();
// 普通方法:也可以有,子类直接继承使用
void sleep() {
System.out.println("睡觉中...");
}
}

// 2. 子类必须实现所有抽象方法,否则子类也得是抽象类
class Dog extends Animal {
@Override
void eat() {
System.out.println("狗啃骨头");
}
}

// 3. 抽象类不能直接实例化
// Animal a = new Animal(); // 编译报错
Animal a = new Dog(); // 向上转型,多态
a.eat(); // 输出:狗啃骨头

// 4. 抽象类可以有构造方法,供子类 super() 调用
abstract class Shape {
String color;
public Shape(String color) {
this.color = color; // 子类构造时通过 super("红色") 调用
}
}

// 5. 抽象类中可以定义 static 成员,通过类名直接访问
abstract class Util {
static final double PI = 3.14;
static void help() {
System.out.println("工具方法");
}
abstract void draw();
}
// Util.help(); → 可以直接调用,不需要对象

// 注意:
// Java 没有抽象属性
// 抽象类继承非抽象类
class Parent {
void a() { System.out.println("a"); }
}
abstract class Child extends Parent {
abstract void b(); // 自己再加抽象方法
}
class GrandChild extends Child {
void b() { System.out.println("b"); }
}

接口

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
interface Animal {
// 1. 抽象方法(默认 public abstract,写不写都一样)
void eat();
public abstract void run();

// 2. 常量(默认 public static final,写不写都一样)
int MAX_AGE = 100;

// 3. 默认方法(Java 8+,有实现,实现类可以不调)
// 如果实现了多个接口,多个接口默认方法同名,子类必须重写
default void sleep() {
System.out.println("睡觉");
test1();
}

// 4. 静态方法(Java 8+,通过接口名调用)
static void info() {
System.out.println("这是动物接口");
test2();
}

// JDK 9 私有方法
private void test1() {} // 不需要 default
private static void test2() {}
}

// ========== 实现接口 ==========
class Dog implements Animal {
@Override
public void eat() { // 必须实现抽象方法
System.out.println("吃骨头");
}

@Override
public void run() {
System.out.println("跑");
}
// sleep() 可以不重写,用默认的
}

// ========== 使用 ==========
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.eat(); // 吃骨头
d.sleep(); // 睡觉(默认方法)
Animal.info(); // 静态方法用接口名调
System.out.println(Animal.MAX_AGE); // 100
}
}

// 接口可以多继承
interface A { void a(); }
interface B { void b(); }
interface C extends A, B { void c(); }
// 类实现一个继承了多个Interface的子类,抽象的方法都要实现。

// 类:单继承 + 多实现
class Parent { void p() {} }
class Child extends Parent implements A, B {
public void a() {}
public void b() {}
}
// 实现多个 interface 重名了只用重写一次
interface A { void test(); }
interface B { void test(); }
class C implements A, B {
public void test() {} // 一次搞定,两个接口都满足了
}
interface A { int test(); }
interface B { String test(); }
class C implements A, B {
// 编译报错,不可能同时返回 int 又返回 String
}
// 注意:
// 父类 vs 接口同名方法:类优先
// 父类和接口都抽象:实现一次就够

内部类

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
/*
* 四种内部类:成员内部类、静态内部类、局部内部类、匿名内部类
*/
public class Outer {
private int x = 10;
private static int y = 20;

// 成员内部类:不能用 static 修饰,可访问外部类所有成员
// 实例化方式:外部类实例.new 内部类()
// JDK16 之前不能定义静态变量
class MemberInner {
private int x = 10;
static int a = 1;
void show() {
System.out.println("成员内部类: x=" + x + ", y=" + y);
System.out.println(Outer.this.x); // 获取外部类同名属性
// System.out.println(XXX.this.x); // 多层也是这个写法
}
}

// 静态内部类:必须用static修饰,只能访问外部类静态成员
// 实例化方式:直接 new 外部类.内部类()
static class StaticInner {
void show() {
System.out.println("静态内部类: y=" + y);
}
}

void method() {
int local = 30; // 隐式final

// 局部内部类:定义在方法内,作用域仅限该方法
// 访问局部变量必须是final(Java8+隐式final也行)
// 和变量一样不能添加修饰符 public 等,类里面可以添加
class LocalInner {
void show() {
System.out.println("局部内部类: local=" + local);
}
}
new LocalInner().show();

// 重点掌握
// 匿名内部类:没有类名,定义和实例化一步完成
// 常用于回调、监听器,Java8后可用Lambda替代(函数式接口)
// new 类名或者接口() {}
Runnable r = new Runnable() {
public void run() {
System.out.println("匿名内部类: local=" + local);
}
};
r.run();

// 没写 final,并且尝试修改,必须是 final 或 effectively final
int b = 10;
b = 20; // 这里修改了b的值
Runnable r2 = new Runnable() {
public void run() {
System.out.println(b); // 编译报错!Variable used in lambda expression should be final or effectively final
}
};
}

public static void main(String[] args) {
new Outer().new MemberInner().show(); // 成员内部类
new Outer.StaticInner().show(); // 静态内部类
new Outer().method(); // 局部内部类 & 匿名内部类
}
}

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
public static int abs(int a) // 返回参数的绝对值
public static double ceil(double a) // 返回大于或等于参数的最小整数
public static double floor(double a) // 返回小于或等于参数的最大整数
public static int round(float a) // 按照四舍五入返回最接近参数的int类型的值(float 返回 int 类型、double 返回 long 类型)
public static int max(int a,int b) // 获取两个int值中的较大值
public static int min(int a,int b) // 获取两个int值中的较小值
public static double pow (double a,double b) // 计算a的b次幂的值
public static double random() // 返回一个[0.0,1.0)的随机值

// 用法
// abs() 返回绝对值,负数变正数
System.out.println(Math.abs(-5)); // 5
System.out.println(Math.abs(Integer.MIN_VALUE)); // bug 最小值没有对应的正数 int -2147483648(还是负数!)

Math.absExact(Integer.MIN_VALUE); // JDK 15+ 安全写法,抛出 ArithmeticException
// ceil() 向上取整,返回>=参数的最小整数(返回double)
System.out.println(Math.ceil(3.2)); // 4.0
// floor() 向下取整,返回<=参数的最大整数(返回double)
System.out.println(Math.floor(3.8)); // 3.0
// round() 四舍五入,返回最接近的int值
System.out.println(Math.round(3.5f)); // 4
// max() 取两数中的较大值
System.out.println(Math.max(10, 20)); // 20
// min() 取两数中的较小值
System.out.println(Math.min(10, 20)); // 10
// pow() 计算a的b次幂(返回double)
System.out.println(Math.pow(2, 3)); // 8.0
// random() 返回[0.0, 1.0)之间的随机小数
System.out.println(Math.random()); // 例如 0.7326...

/* 注意:
ceil 和 floor 返回的是 double 类型,不是 int
round 接收 float 返回 int;若传入 double 则返回 long(Math.round(double))
random() 的范围是 [0.0, 1.0),包含0.0但不包含1.0
*/

System

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static long currentTimeMillis() // 获取当前时间所对应的毫秒值(当前时间为0时区所对应的时间即就是英国格林尼治天文台旧址所在位置)
public static void exit(int status) // 终止当前正在运行的Java虚拟机,0表示正常退出,非零表示异常退出
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); // 进行数值元素copy

// currentTimeMillis() 获取当前时间毫秒值(常用于计算代码执行耗时)
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) { } // 模拟一段代码
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");

// arraycopy() 数组拷贝(src, src起始位置, dest, dest起始位置, 拷贝长度)
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(src, 1, dest, 0, 3); // 从src索引1开始,拷贝3个元素到dest索引0处
// dest = {2, 3, 4, 0, 0}
System.out.println(java.util.Arrays.toString(dest));

// exit() 终止JVM(0=正常退出,非0=异常退出)
// System.exit(0); // 取消注释可测试,执行后程序立即终止

Runtime

表示虚拟机的运行环境

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
public static Runtime getRuntime() //当前系统的运行环境对象
public void exit(int status) //停止虚拟机
public int availableProcessors() //获得CPU的线程数
public long maxMemory() //JVM能从系统中获取总内存大小(单位byte)
public long totalMemory() //JVM已经从系统中获取总内存大小(单位byte)
public long freeMemory() //JVM剩余内存大小(单位byte)
public Process exec(String command) //运行cmd命令

//1.获取Runtime的对象
Runtime r1 =Runtime.getRuntime();
//2.exit 停止虚拟机
Runtime.getRuntime().exit(0);
//System.out.println("看看我执行了吗?");
//3.获得CPU的线程数
System.out.println(Runtime.getRuntime().availableProcessors());//8
//4.总内存大小,单位byte字节
System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);//4064
//5.已经获取的总内存大小,单位byte字节
System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);//254
//6.剩余内存大小
System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);//251
//7.运行cmd命令
//shutdown :关机
//加上参数才能执行
//-s :默认在1分钟之后关机
//-s -t 指定时间 : 指定关机时间
//-a :取消关机操作
//-r: 关机并重启
Runtime.getRuntime().exec("shutdown -s -t 3600");

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public String toString() //返回该对象的字符串表示形式(可以看做是对象的内存地址值)
public boolean equals(Object obj) //比较两个对象地址值是否相等;true表示相同,false表示不相同
protected Object clone() //对象克隆,调用不了需要重写

// 默认:类名@哈希码(如 Student@1b6d3586)
Student s1 = new Student("张三", 20);
System.out.println(s1); // 自动调用 toString()
// 默认:比较地址值(和 == 一样)
Student s2 = new Student("张三", 20);
System.out.println(s1 == s2); // false(地址不同)

// clone() 需要实现 Cloneable 接口,否则会抛 CloneNotSupportedException
class Student implements Cloneable {
String name;
int age;

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

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

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student s = (Student) obj;
return age == s.age && name.equals(s.name);
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}

public class ObjectDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("张三", 20);
// toString() 返回对象的字符串表示(默认是 类名@哈希码)
System.out.println(s1.toString()); // Student{name='张三', age=20}
// equals() 比较两个对象是否"相等"(默认比较地址值)
Student s2 = new Student("张三", 20);
System.out.println(s1.equals(s2)); // true(重写了equals,比较内容)
// clone() 克隆出一个新对象(地址不同,内容相同)
Student s3 = (Student) s1.clone();
System.out.println(s3 == s1); // false(地址不同,是新对象)
System.out.println(s3.equals(s1)); // true(内容相同)
}
}

Objects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static String toString(Object o) // 获取对象的字符串表现形式
public static boolean equals(Object a, Object b) // 比较两个对象是否相等
public static boolean isNull(Object obj) // 判断对象是否为null
public static boolean nonNull(Object obj) // 判断对象是否不为null

// toString() 获取对象的字符串表示(支持null安全)
String s = null;
System.out.println(Objects.toString(s)); // "null"(不报错)
System.out.println(Objects.toString(s, "默认值")); // "默认值"(null时返回指定默认值)

// equals() 比较两个对象是否相等(支持null安全)
String a = "hello";
String b = null;
System.out.println(Objects.equals(a, b)); // false
System.out.println(Objects.equals(b, null)); // true(两个都是null也算相等)
// 对比:a.equals(b) 如果a是null会抛NullPointerException

// isNull() 判断是否为null
System.out.println(Objects.isNull(b)); // true

// nonNull() 判断是否不为null
System.out.println(Objects.nonNull(a)); // true

浅拷贝深拷贝

1
2
// Object 里面的 clone 是浅拷贝
// 深拷贝库:Gson

BigInteger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public BigInteger(int num, Random rnd) //获取随机大整数,范围:[0 ~ 2的num次方-1]
public BigInteger(String val) //获取指定的大整数
public BigInteger(String val, int radix) //获取指定进制的大整数
// 下面这个不是构造,而是一个静态方法获取BigInteger对象
public static BigInteger valueOf(long val) //静态方法获取BigInteger的对象,内部有优化

// 1. 随机大整数:范围 [0, 2^num - 1]
BigInteger r = new BigInteger(128, new Random());
// 2. 十进制字符串 → BigInteger
BigInteger a = new BigInteger("123456789012345678901234567890");
// 3. 指定进制字符串 → BigInteger(第二个参数为进制)
BigInteger b = new BigInteger("FF", 16); // 十六进制FF → 255
// 4. 静态方法 valueOf(内部对 -16~16 有缓存优化,推荐优先使用)
BigInteger c = BigInteger.valueOf(100);
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 BigInteger add(BigInteger val) //加法
public BigInteger subtract(BigInteger val) //减法
public BigInteger multiply(BigInteger val) //乘法
public BigInteger divide(BigInteger val) //除法
public BigInteger[] divideAndRemainder(BigInteger val) //除法,获取商和余数
public boolean equals(Object x) //比较是否相同
public BigInteger pow(int exponent) //次幂、次方
public BigInteger max/min(BigInteger val) //返回较大值/较小值
public int intValue(BigInteger val) //转为int类型整数,超出范围数据有误

// ===================== 基本运算 =====================
BigInteger x = BigInteger.valueOf(17);
BigInteger y = BigInteger.valueOf(5);

x.add(y); // 加:22
x.subtract(y); // 减:12
x.multiply(y); // 乘:85
x.divide(y); // 除:3(截断取整)
x.remainder(y); // 余数:2

// 同时获取商和余数:[0]=商,[1]=余数
BigInteger[] qr = x.divideAndRemainder(y);
// qr[0] = 3, qr[1] = 2

// ===================== 比较与幂运算 =====================

x.equals(y); // 值比较(不要用 == ,== 比较的是引用地址)
x.pow(3); // 幂:17^3 = 4913
x.max(y); // 返回较大值:17
x.min(y); // 返回较小值:5

// ===================== 类型转换 =====================

int v = x.intValue(); // 转int,超出int范围数据会出错!

// ===================== 注意事项 =====================
// 1. BigInteger 是不可变类,每次运算都会返回新对象,原值不变
// 必须用变量接收返回值:a = a.add(b),而不是 a.add(b)
// 2. equals 比较的是数值内容,== 比较的是对象引用地址
// 3. intValue() 等窄化转换会丢失精度,大数会溢出
// 4. valueOf 内部有缓存(-16~16),小数值场景优先使用
// 5. 构造器传入 null 或非法字符串会抛 NumberFormatException

BigDecimal

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
// 1. 推荐:字符串创建(精确,无精度丢失)
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");

// 2. double创建(有精度问题,不推荐直接用于精确计算)
BigDecimal c = new BigDecimal(0.1); // 实际值 ≠ 0.1,存在精度偏差

// 3. 静态方法 valueOf(内部将double转字符串再创建,能保证精确,推荐使用)
BigDecimal d = BigDecimal.valueOf(0.1); // 等价于 new BigDecimal("0.1")

// ===================== 基本运算 =====================

BigDecimal x = new BigDecimal("10");
BigDecimal y = new BigDecimal("3");

x.add(y); // 加:13
x.subtract(y); // 减:7
x.multiply(y); // 乘:30

// ===================== 除法(重点) =====================

// 除不尽时直接 divide 会抛 ArithmeticException!
// x.divide(y); // 10/3 除不尽,直接报错

// 必须指定:保留几位小数 + 舍入模式
x.divide(y, 2, RoundingMode.HALF_UP); // 3.33(四舍五入保留2位)
x.divide(y, 4, RoundingMode.DOWN); // 3.3333(直接截断)
x.divide(y, 2, RoundingMode.CEILING); // 3.34(向上取整)
x.divide(y, 2, RoundingMode.FLOOR); // 3.33(向下取整,负数和DOWN有区别)
// ===================== 比较 =====================

// equals:比较值 + 精度(scale不同则false)
new BigDecimal("1.0").equals(new BigDecimal("1.00")); // false(精度不同)

// compareTo:只比较数值大小(推荐用于大小判断)
new BigDecimal("1.0").compareTo(new BigDecimal("1.00")); // 0(相等)
// 返回值:>0 大于,==0 相等,<0 小于

// ===================== 注意事项 =====================
// 1. BigDecimal 是不可变类,运算必须接收返回值:a = a.add(b)
// 2. 创建时优先用字符串构造或 valueOf(double),避免 new BigDecimal(double)
// 3. divide 除不尽必须指定精度和舍入模式,否则抛异常
// 4. 比较大小用 compareTo,不要用 equals(equals 会比较精度)
// 5. 常用舍入模式:
// HALF_UP → 四舍五入
// DOWN → 直接截断(抹零)
// CEILING → 向上取整(正数进位)
// FLOOR → 向下取整(负数进位)

正则表达式

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
// 使用 matches 方法 价于 JS 的 /^...$/
System.out.println(qq.matches("[1-9]\\d{5,19}"));

// && 是字符集交集,js 没有
// Java:取 a-z 和 def 的交集 → 匹配 d、e、f
String regex = "[a-z&&[def]]";
"a".matches(regex); // false
"d".matches(regex); // true

// Pattern、Matcher
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo {
public static void main(String[] args) {
// Pattern:编译正则表达式(不可变对象,线程安全,可复用)
Pattern pattern = Pattern.compile("\\d+");

// Matcher:用 Pattern 对目标字符串做匹配操作
Matcher matcher = pattern.matcher("abc123def456ghi789");

// find():逐个查找匹配项,找到返回 true,每次调用位置自动后移
while (matcher.find()) {
System.out.println(matcher.group()); // 当前匹配的文本
System.out.println("起始: " + matcher.start()); // 返回匹配文本的起始索引(包含)
System.out.println("结束: " + matcher.end()); // 返回匹配文本的结束索引(不包含)

// 第1次 find: group=123, start=3, end=6
// 第2次 find: group=456, start=9, end=12
// 第3次 find: group=789, start=15, end=18
}
// 输出: 123, 456, 789
}
}

String s = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," + "因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";

// (?=...) 正向前瞻
// ?理解为前面的数据Java
// =表示在Java后面要跟随的数据,但是在获取的时候,只获取前半部分
// 爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
String regex1 = "((?i)Java)(?=8|11|17)"; // (?i) js 中不支持
// 爬取版本号为8,11,17的Java文本
String regex2 = "((?i)Java)(8|11|17)";
// 非捕获分组
String regex3 = "((?i)Java)(?:8|11|17)";
// (?!...) 负向前瞻
// 爬取除了版本号为8,11.17的Java文本
String regex4 = "((?i)Java)(?!8|11|17)";
/*
(?:...) 是普通分组,会参与匹配但不分配编号;
(?=...) 和 (?!...) 是先行断言,只判断后面是否符合条件,不纳入匹配结果,也不占编号。
*/
// 非捕获分组:参与匹配,但不分配编号
Matcher m1 = Pattern.compile("(a)(?:b)(c)").matcher("abc");
m1.find();
m1.group(1); // "a"
m1.group(2); // "c" ← (?:b) 不占编号,后面的 (c) 顺延为第 2 组
m1.group(); // abc 匹配
// 正向先行断言:要求后面必须是某内容,自身不进结果
Matcher m2 = Pattern.compile("\\d+(?=元)").matcher("99元");
m2.find();
m2.group(); // "99"
// 负向先行断言:要求后面不能是某内容
Matcher m3 = Pattern.compile("\\d+(?!元)").matcher("99元 5kg");
while (m3.find()) { }
m3.group(); // "5" ← "99" 后面是"元",被排除
// 替换对比:断言不进入结果,所以"元"会被保留
"99元".replaceAll("\\d+元", ""); // ""
"99元".replaceAll("\\d+(?=元)", ""); // "元"
// 断言内可以嵌套普通分组:group(1) 能取到值
Matcher m5 = Pattern.compile("(?=(\\d+)元)").matcher("99元");
m5.find();
m5.group(); // "99元"
m5.group(1); // "99"
// 经典用途:多个条件从开头叠加,互不干扰(密码校验)
"abc12345".matches("^(?=.*\\d)(?=.*[a-z]).{8,}$"); // true

// 贪婪爬取和非贪婪爬取
/*
只写+和表示贪婪匹配,如果在+和后面加问号表示非贪婪爬取
+? 非贪婪匹配
*? 非贪婪匹配
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
*/

String使用正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// public String[] split(String regex)
//参数regex表示正则表达式。可以将当前字符串中匹配regex正则表达式的符号作为"分隔符"来切割字符串。
String s = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
String[] arr = s.split("[\\w&&[^_]]+");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

// replaceAll()
// public String replaceAll(String regex,String newStr)
//参数regex表示一个正则表达式。可以将当前字符串中匹配regex正则表达式的字符串替换为newStr。
String s = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
String result1 = s.replaceAll("[\\w&&[^_]]+", "vs");
System.out.println(result1);

正则表达式分组

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
// 只看左括号,不看有括号,按照左括号的顺序,从左往右,依次为第一组,第二组,第三组等等

//需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
//举例: a123a b456b 17891 &abc& a123b(false)
// \\组号:表示把第X组的内容再出来用一次
String regex1 = "(.).+\\1";
System.out.println("a123a".matches(regex1));
System.out.println("b456b".matches(regex1));
System.out.println("17891".matches(regex1));
System.out.println("&abc&".matches(regex1));
System.out.println("a123b".matches(regex1));
System.out.println("--------------------------");

//需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
//举例: abc123abc b456b 123789123 &!@abc&!@ abc123abd(false)
String regex2 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
System.out.println("---------------------");

//需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//举例: aaa123aaa bbb456bbb 111789111 &&abc&&
//(.):把首字母看做一组
// \\2:把首字母拿出来再次使用
// *:作用于\\2,表示后面重复的内容出现日次或多次
String regex3 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));

// 正则表达式内部使用(\\组号)
// 正则表达式外部使用($组号)
String str = "我要学学编编编编程程程程程程";
//需求:把重复的内容 替换为 单个的
//学学 学
//编编编编 编
//程程程程程程 程
// (.)表示把重复内容的第一个字符看做一组
// \\1表示第一字符再次出现
// + 至少一次
// $1 表示把正则表达式中第一组的内容,再拿出来用
String result = str.replaceAll("(.)\\1+", "$1");
System.out.println(result);

// 非捕获组
// 身份证号码的简易正则表达式
// 非捕获分组:仅仅是把数据括起来
// 特点:不占用组号
// 这里\\1报错原因:(?:)就是非捕获分组,此时是不占用组号的。
// (?:) (?=) (?!)都是非捕获分组//更多的使用第一个
// String regex1 ="[1-9]\\d{16}(?:\\d|x|x)\\1";
String regex2 ="[1-9]\\d{16}(\\d Xx)\\1";
// ^([01]\d|2[0-3]):[0-5]\d:[@-5]\d$
System.out.println("41080119930228457x".matches(regex2));

Date类

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
import java.util.Date;

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

// ==================== 创建 Date ====================
Date now = new Date(); // 当前时间
Date date = new Date(1700000000000L); // 指定毫秒值(从1970-01-01 00:00:00起)

// ==================== 常用方法 ====================
long time = now.getTime(); // 获取毫秒值(最常用)
now.setTime(1700000000000L); // 设置毫秒值
boolean before = date.before(now); // date 是否在 now 之前
boolean after = date.after(now); // date 是否在 now 之后
int cmp = date.compareTo(now); // 比较:负数=早,0=相等,正数=晚

// ==================== 日期计算(通过毫秒值) ====================
long oneDay = 24 * 60 * 60 * 1000L; // 一天的毫秒值
Date tomorrow = new Date(now.getTime() + oneDay); // 加一天
Date lastWeek = new Date(now.getTime() - 7 * oneDay); // 减7天

// ==================== toString() ====================
System.out.println(now.toString());
// 输出类似: Wed Jan 15 14:30:00 CST 2025
}
}

SimpleDateFormat

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

// 注意:SimpleDateFormat 是线程不安全的,多线程环境请改用 DateTimeFormatter (Java8+)

// 【关键构造方法】
SimpleDateFormat sdf1 = new SimpleDateFormat(); // 默认格式:yy/MM/dd HH:mm:ss
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 自定义格式(最常用)

// 【常用方法】
Date now = new Date();

// Date → String(格式化)
String str = sdf2.format(now); // "2026-09-09 19:14:00"

// String → Date(解析)
Date date = sdf2.parse("2026-09-09 19:14:00"); // 抛出 ParseException,需 try-catch

// 【常用格式符号速查】
// yyyy 年 | MM 月 | dd 日 | HH 24时制 | hh 12时制
// mm 分 | ss 秒 | SSS 毫秒 | E 星期 | a AM/PM

Calendar

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
import java.util.Calendar;

/**
* java.util.Calendar 日历类笔记
*
* 概述:抽象类,不能new,通过 Calendar.getInstance() 获取子类 GregorianCalendar 对象
* 注意:月份从0开始(0=一月),星期从1开始(1=周日)
*/
public class CalendarDemo {
public static void main(String[] args) {
Calendar c = Calendar.getInstance(); // 获取当前日历对象

// ========== get:获取字段值 ==========
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH) + 1; // 月份0-11,需+1
int day = c.get(Calendar.DAY_OF_MONTH); // 月中第几天
int hour = c.get(Calendar.HOUR); // 12小时制
int hour24 = c.get(Calendar.HOUR_OF_DAY); // 24小时制
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
int week = c.get(Calendar.DAY_OF_WEEK); // 1=周日, 2=周一...7=周六
System.out.printf("当前:%d-%02d-%02d %02d:%02d:%02d 星期%d%n", year, month, day, hour, minute, second, week);

// ========== set:设置字段值 ==========
c.set(Calendar.YEAR, 1998);
c.set(Calendar.MONTH, 2); // 3月,月份从0开始所以写2
c.set(Calendar.DAY_OF_MONTH, 18);
// 快捷写法:set(year, month, day)
// c.set(1998, 2, 18);
System.out.println("1998-03-18是星期" + "日一二三四五六".charAt(c.get(Calendar.DAY_OF_WEEK) - 1));

// ========== add:字段增减(自动进位) ==========
c.setTimeInMillis(System.currentTimeMillis()); // 重置为当前时间
c.add(Calendar.DAY_OF_MONTH, 200); // 加200天,负数则减
System.out.printf("200天后:%d-%02d-%02d%n", c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));

// ========== 其他实用方法 ==========
long millis = c.getTimeInMillis(); // 获取毫秒时间戳
c.setTimeInMillis(millis); // 用毫秒值设置时间
c.add(Calendar.MONTH, -1); // 减1个月
int maxDay = c.getActualMaximum(Calendar.DAY_OF_MONTH); // 当月最大天数
int minDay = c.getActualMinimum(Calendar.DAY_OF_MONTH); // 当月最小天数(通常为1)

System.out.printf("上月最后一天是%d号,时间戳:%d%n", maxDay, millis);

// ========== Calendar 与 Date 互转 ==========
java.util.Date date = c.getTime(); // Calendar → Date
c.setTime(date); // Date → Calendar
}
}

// 注意:
// add:安全截断,不溢出
Calendar c1 = Calendar.getInstance();
c1.set(2026, 0, 31); // 1月31日
c1.add(Calendar.MONTH, 1); // 加1月 → 2月28日(截断到月末)

// set:惰性计算,会溢出
Calendar c2 = Calendar.getInstance();
c2.set(2026, 0, 31); // 1月31日
c2.set(Calendar.MONTH, 1); // 设2月 → 内部存"2月31日"
// get()时:2月28天 + 溢出3天 → 3月3日
// 记忆:add截断安全,set溢出踩坑。用set改月时顺手设日。

JDK8时间相关类

JDK8时间类类名 作用
ZoneId 时区
Instant 时间戳
ZoneDateTime 带时区的时间
DateTimeFormatter 用于时间的格式化和解析
LocalDate 年、月、日
LocalTime 时、分、秒
LocalDateTime 年、月、日、时、分、秒
Duration 时间间隔(秒,纳,秒)
Period 时间间隔(年,月,日)
ChronoUnit 时间间隔(所有单位)

ZoneId 时区

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
static Set<string> getAvailableZoneIds() 获取Java中支持的所有时区
static ZoneId systemDefault() 获取系统默认时区
static Zoneld of(string zoneld) 获取一个指定时区
*/

//1.获取所有的时区名称
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds.size());//600
//2.获取当前系统的默认时区
ZoneId zoneId = ZoneId.systemDefault();
//3.获取指定的时区
ZoneId zoneId1 = ZoneId.of("Asia/Pontianak");

Instant 时间戳

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
/*
static Instant now() 获取当前时间的Instant对象(标准时间)
static Instant ofXxxx(long epochMilli) 根据(秒/毫秒/纳秒)获取Instant对象
ZonedDateTime atZone(ZoneIdzone) 指定时区
boolean isxxx(Instant otherInstant) 判断系列的方法
Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant plusXxx(long millisToSubtract) 增加时间系列的方法
*/
//1.获取当前时间的Instant对象(标准时间)
Instant now = Instant.now();
System.out.println(now);

//2.根据(秒/毫秒/纳秒)获取Instant对象
Instant instant1 = Instant.ofEpochMilli(0L);
System.out.println(instant1);//1970-01-01T00:00:00z

Instant instant2 = Instant.ofEpochSecond(1L);
System.out.println(instant2);//1970-01-01T00:00:01Z

Instant instant3 = Instant.ofEpochSecond(1L, 1000000000L);
System.out.println(instant3);//1970-01-01T00:00:02Z

//3. 指定时区
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time);

//4.isXxx 判断
Instant instant4=Instant.ofEpochMilli(0L);
Instant instant5 =Instant.ofEpochMilli(1000L);

//5.用于时间的判断
//isBefore:判断调用者代表的时间是否在参数表示时间的前面
boolean result1=instant4.isBefore(instant5);
System.out.println(result1);//true

//isAfter:判断调用者代表的时间是否在参数表示时间的后面
boolean result2 = instant4.isAfter(instant5);
System.out.println(result2);//false

//6.Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant instant6 =Instant.ofEpochMilli(3000L);
System.out.println(instant6);//1970-01-01T00:00:03Z
Instant instant7 =instant6.minusSeconds(1);
System.out.println(instant7);//1970-01-01T00:00:02Z

ZoneDateTime 带时区的时间

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
/*
static ZonedDateTime now() 获取当前时间的ZonedDateTime对象
static ZonedDateTime ofXxxx(。。。) 获取指定时间的ZonedDateTime对象
ZonedDateTime withXxx(时间) 修改时间系列的方法
ZonedDateTime minusXxx(时间) 减少时间系列的方法
ZonedDateTime plusXxx(时间) 增加时间系列的方法
*/
//1.获取当前时间对象(带时区)
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);

//2.获取指定的时间对象(带时区)1/年月日时分秒纳秒方式指定
ZonedDateTime time1 = ZonedDateTime.of(2023, 10, 1, 11, 12, 12, 0, ZoneId.of("Asia/Shanghai"));
System.out.println(time1);

//通过Instant + 时区的方式指定获取时间对象
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2);

//3.withXxx 修改时间系列的方法
ZonedDateTime time3 = time2.withYear(2000);
System.out.println(time3);

//4. 减少时间
ZonedDateTime time4 = time3.minusYears(1);
System.out.println(time4);

//5.增加时间
ZonedDateTime time5 = time4.plusYears(1);
System.out.println(time5);

DateTimeFormatter 用于时间的格式化和解析

1
2
3
4
5
6
7
8
9
10
11
/*
static DateTimeFormatter ofPattern(格式) 获取格式对象
String format(时间对象) 按照指定方式格式化
*/
//获取时间对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));

// 解析/格式化器
DateTimeFormatter dtf1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm;ss EE a");
// 格式化
System.out.println(dtf1.format(time));

LocalDate 年、月、日

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
//1.获取当前时间的日历对象(包含 年月日)
LocalDate nowDate = LocalDate.now();
//2.获取指定的时间的日历对象
LocalDate ldDate = LocalDate.of(2023, 1, 1);
System.out.println("指定日期:" + ldDate);

System.out.println("=============================");

//3.get系列方法获取日历中的每一个属性值//获取年
int year = ldDate.getYear();
System.out.println("year: " + year);
//获取月//方式一:
Month m = ldDate.getMonth();
System.out.println(m);
System.out.println(m.getValue());

//方式二:
int month = ldDate.getMonthValue();
System.out.println("month: " + month);

//获取日
int day = ldDate.getDayOfMonth();
System.out.println("day:" + day);

//获取一年的第几天
int dayofYear = ldDate.getDayOfYear();
System.out.println("dayOfYear:" + dayofYear);

//获取星期
DayOfWeek dayOfWeek = ldDate.getDayOfWeek();
System.out.println(dayOfWeek);
System.out.println(dayOfWeek.getValue());

//is开头的方法表示判断
System.out.println(ldDate.isBefore(ldDate));
System.out.println(ldDate.isAfter(ldDate));

//with开头的方法表示修改,只能修改年月日
LocalDate withLocalDate = ldDate.withYear(2000);
System.out.println(withLocalDate);

//minus开头的方法表示减少,只能减少年月日
LocalDate minusLocalDate = ldDate.minusYears(1);
System.out.println(minusLocalDate);

//plus开头的方法表示增加,只能增加年月日
LocalDate plusLocalDate = ldDate.plusDays(1);
System.out.println(plusLocalDate);

//-------------
// 判断今天是否是你的生日
LocalDate birDate = LocalDate.of(2000, 1, 1);
LocalDate nowDate1 = LocalDate.now();

MonthDay birMd = MonthDay.of(birDate.getMonthValue(), birDate.getDayOfMonth());
MonthDay nowMd = MonthDay.from(nowDate1);

System.out.println("今天是你的生日吗? " + birMd.equals(nowMd));

LocalTime 时、分、秒

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
// 获取本地时间的日历对象。(包含 时分秒)
LocalTime nowTime = LocalTime.now();
System.out.println("今天的时间:" + nowTime);

int hour = nowTime.getHour();//时
System.out.println("hour: " + hour);

int minute = nowTime.getMinute();//分
System.out.println("minute: " + minute);

int second = nowTime.getSecond();//秒
System.out.println("second:" + second);

int nano = nowTime.getNano();//纳秒
System.out.println("nano:" + nano);
System.out.println("------------------------------------");
System.out.println(LocalTime.of(8, 20));//时分
System.out.println(LocalTime.of(8, 20, 30));//时分秒
System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
LocalTime mTime = LocalTime.of(8, 20, 30, 150);

//is系列的方法
System.out.println(nowTime.isBefore(mTime));
System.out.println(nowTime.isAfter(mTime));

//with系列的方法,只能修改时、分、秒
System.out.println(nowTime.withHour(10));

//plus系列的方法,只能修改时、分、秒
System.out.println(nowTime.plusHours(10));

LocalDateTime 年、月、日、时、分、秒

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
// 当前时间的的日历对象(包含年月日时分秒)
LocalDateTime nowDateTime = LocalDateTime.now();

System.out.println("今天是:" + nowDateTime);
System.out.println(nowDateTime.getYear());//年
System.out.println(nowDateTime.getMonthValue());//月
System.out.println(nowDateTime.getDayOfMonth());//日
System.out.println(nowDateTime.getHour());//时
System.out.println(nowDateTime.getMinute());//分
System.out.println(nowDateTime.getSecond());//秒
System.out.println(nowDateTime.getNano());//纳秒
// 日:当年的第几天
System.out.println("dayofYear:" + nowDateTime.getDayOfYear());
//星期
System.out.println(nowDateTime.getDayOfWeek());
System.out.println(nowDateTime.getDayOfWeek().getValue());
//月份
System.out.println(nowDateTime.getMonth());
System.out.println(nowDateTime.getMonth().getValue());

LocalDate ld = nowDateTime.toLocalDate();
System.out.println(ld);

LocalTime lt = nowDateTime.toLocalTime();
System.out.println(lt.getHour());
System.out.println(lt.getMinute());
System.out.println(lt.getSecond());

// 格式输出
LocalDateTime ldt = LocalDateTime.of(2026, 9, 20, 15, 23, 30);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s2 = ldt.format(fmt); // 2026-09-20 15:23:30

Duration 时间间隔(秒,纳,秒)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 本地日期时间对象。
LocalDateTime today = LocalDateTime.now();
System.out.println(today);

// 出生的日期时间对象
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
System.out.println(birthDate);

Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数
System.out.println("相差的时间间隔对象:" + duration);

System.out.println("============================================");
System.out.println(duration.toDays());//两个时间差的天数
System.out.println(duration.toHours());//两个时间差的小时数
System.out.println(duration.toMinutes());//两个时间差的分钟数
System.out.println(duration.toMillis());//两个时间差的毫秒数
System.out.println(duration.toNanos());//两个时间差的纳秒数

Period 时间间隔(年,月,日)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 当前本地 年月日
LocalDate today = LocalDate.now();
System.out.println(today);

// 生日的 年月日
LocalDate birthDate = LocalDate.of(2000, 1, 1);
System.out.println(birthDate);

Period period = Period.between(birthDate, today);//第二个参数减第一个参数

System.out.println("相差的时间间隔对象:" + period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());

System.out.println(period.toTotalMonths());

ChronoUnit 时间间隔(所有单位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 当前时间
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 生日时间
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1,0, 0, 0);
System.out.println(birthDate);

System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));
System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));
System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));
System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));
System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));
System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));
System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));
System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));
System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));
System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));
System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));
System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));
System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));
System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));
System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));

包装类

方法名 说明
public Integer(int value) 根据 int 值创建 Integer 对象(过时)
public Integer(String s) 根据 String 值创建 Integer 对象(过时)
public static Integer valueOf(int i) 返回表示指定的 int 值的 Integer 实例
public static Integer valueOf(String s) 返回保存指定String值的 Integer 对象
public static string toBinarystring(int i) 得到二进制
public static string toOctalstring(int i) 得到八进制
public static string toHexstring(int i) 得到十六进制
public static int parseInt(string s) 将字符串类型的整数转成int类型的整数
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 Integer(int value):根据 int 值创建 Integer 对象(过时)
Integer i1 = new Integer(100);
//public Integer(String s):根据 String 值创建 Integer 对象(过时)
Integer i2 = new Integer("100");

//public static Integer valueOf(int i):返回表示指定的 int 值的 Integer 实例
Integer i3 = Integer.valueOf(100);
//public static Integer valueOf(String s):返回保存指定String值的Integer对象
Integer i4 = Integer.valueOf("100");
// valueOf 在 -128 到 127 之前去缓存,同一个地址值

// jdk 5 以前的写法
int x = i.intValue();
// 自动装箱与自动拆箱
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
// 加法运算完成后,再次装箱,把基本数值转成对象。

// 基本类型与字符串之间的转换
// int --- String
int number = 100;
//方式1:直接在数字后加一个空字符串
String s1 = number + "";
System.out.println(s1);
//方式2:通过String类静态方法valueOf()
//public static String valueOf(int i)
String s2 = String.valueOf(number);
System.out.println(s2);
/* String转换成基本类型
public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。
*/
//String --- int
String s = "100";
//方式1:先将字符串数字转成Integer,再调用valueOf()方法
Integer i = Integer.valueOf(s);
//public int intValue()
int x = i.intValue();
System.out.println(x);
//方式2:通过Integer静态方法parseInt()进行转换
//public static int parseInt(String s)
int y = Integer.parseInt(s);
System.out.println(y);

算法

二分查找

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
// 元素必须是有序的,从小到大,或者从大到小都是可以的
// 如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的。

int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};
System.out.println(binarySearch(arr, 150));

public static int binarySearch(int[] arr, int number){
//1.定义两个变量记录要查找的范围
int min = 0;
int max = arr.length - 1;

//2.利用循环不断的去找要查找的数据
while(true){
if(min > max){
return -1;
}
//3.找到min和max的中间位置
int mid = (min + max) / 2;
//4.拿着mid指向的元素跟要查找的元素进行比较
if(arr[mid] > number){
//4.1 number在mid的左边
//min不变,max = mid - 1;
max = mid - 1;
}else if(arr[mid] < number){
//4.2 number在mid的右边
//max不变,min = mid + 1;
min = mid + 1;
}else{
//4.3 number跟mid指向的元素一样
//找到了
return mid;
}
}
}

插入排序

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
/*
插入排序:
将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。
遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。
N的范围:0~最大索引
*/
int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
//1.找到无序的哪一组数组是从哪个索引开始的。
int startIndex = -1;
for (int i = 0; i < arr.length; i++) {
if(arr[i] > arr[i + 1]){
startIndex = i + 1;
break;
}
}
//2.遍历从startIndex开始到最后一个元素,依次得到无序的哪一组数据中的每一个元素
for (int i = startIndex; i < arr.length; i++) {
//记录当前要插入数据的索引
int j = i;
while(j > 0 && arr[j] < arr[j - 1]){
//交换位置
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
j--;
}
}

// 或者
for (int i = startIndex; i < arr.length; i++) {
int num = arr[i]; // 抽出来,手里攥着
int j = i - 1;
while (j >= 0 && num < arr[j]) {
arr[j + 1] = arr[j]; // 大的往右挪一格,空位左移
j--;
}
arr[j + 1] = num; // 填坑(永远是 j+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
/*
* 参数一:我们要排序的数组
* 参数二:要排序数组的起始索引
* 参数三:要排序数组的结束索引
* */
public static void quickSort(int[] arr, int i, int j) {
//定义两个变量记录要查找的范围
int start = i;
int end = j;
if(start > end){
//递归的出口
return;
}
//记录基准数
int baseNumber = arr[i];
//利用循环找到要交换的数字
while(start != end){
//利用end,从后往前开始找,找比基准数小的数字
while(true){
if(end <= start || arr[end] < baseNumber){
break;
}
end--;
}
System.out.println(end);
//利用start,从前往后找,找比基准数大的数字
while(true){
if(end <= start || arr[start] > baseNumber){
break;
}
start++;
}
//把end和start指向的元素进行交换
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
//当start和end指向了同一个元素的时候,那么上面的循环就会结束
//表示已经找到了基准数在数组中应存入的位置
//基准数归位
//就是拿着这个范围中的第一个数字,跟start指向的元素进行交换
int temp = arr[i];
arr[i] = arr[start];
arr[start] = temp;
//确定6左边的范围,重复刚刚所做的事情
quickSort(arr,i,start - 1);
//确定6右边的范围,重复刚刚所做的事情
quickSort(arr,start + 1,j);
}

枚举类

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
/**
* Java 枚举 enum
* 1. 枚举是特殊类,默认继承 java.lang.Enum,不能继承其他类,但可实现接口
* 2. 枚举常量本质是 public static final 的实例
* 3. 可定义字段、构造器(默认 private)、方法
* 4. 常用方法:name()、ordinal()、values()、valueOf()
* 5. switch 支持枚举
*/
public class EnumDemo {

// 简单枚举
enum Color {
RED, GREEN, BLUE;
}

// 带字段、构造器、方法的枚举
enum Status {
SUCCESS(200, "成功"),
NOT_FOUND(404, "未找到"),
ERROR(500, "服务器错误");

private final int code;
private final String msg;

Status(int code, String msg) { // 枚举构造器默认 private
this.code = code;
this.msg = msg;
}

public int getCode() { return code; }
public String getMsg() { return msg; }

public static Status fromCode(int code) {
for (Status s : values()) {
if (s.code == code) return s;
}
throw new IllegalArgumentException("未知 code: " + code);
}
}

// 枚举实现接口
interface Describable {
String desc();
}

enum Level implements Describable {
LOW {
@Override
public String desc() { return "低"; }
},
HIGH {
@Override
public String desc() { return "高"; }
};
}

public static void main(String[] args) {
Color c = Color.RED;

System.out.println(c.name()); // RED
System.out.println(c.ordinal()); // 0

for (Color color : Color.values()) {
System.out.println(color);
}

Color c2 = Color.valueOf("GREEN");

switch (c) {
case RED -> System.out.println("红色");
case GREEN -> System.out.println("绿色");
case BLUE -> System.out.println("蓝色");
}

Status s = Status.fromCode(404);
System.out.println(s.getCode() + " " + s.getMsg()); // 404 未找到

System.out.println(Level.HIGH.desc()); // 高
}
}

Arrays

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
// 1. 打印
Arrays.toString(arr); // arr: 一维数组
Arrays.deepToString(mat); // mat: 多维数组(多维别用 toString)

// 2. 排序(原地,无返回值)
Arrays.sort(arr); // arr: 要排序的数组
Arrays.sort(arr, from, to); // from/to: 排序区间 [from,to)
Arrays.sort(arr, Comparator.reverseOrder()); // arr 必须是包装类型 Integer[]
Arrays.sort(a, new Comparator<Integer>() { // new 接口名<泛型>() { ... }
@Override
public int compare(Integer o1, Integer o2) { // o1=当前元素, o2=下一个元素
return o2.compareTo(o1); // 反过来比 = 降序
}
});
Arrays.parallelSort(arr); // 大数据量才快

// 3. 二分查找(arr 必须先升序排好,否则结果未定义)
int i = Arrays.binarySearch(arr, key); // key: 要找的值;找到返回下标,没找到返回 -(插入点)-1
int i = Arrays.binarySearch(arr, from, to, key); // 只在 [from,to) 内找,该段必须已升序

// 4. 填充
Arrays.fill(arr, val); // val: 填入的值(基本类型直接写字面量即可)
Arrays.fill(arr, from, to, val); // 只填 [from,to)

// 5. 复制(返回新数组,原数组不变)
int[] b = Arrays.copyOf(arr, newLen); // newLen: 新数组长度;长了补0/null,短了截断
int[] c = Arrays.copyOfRange(arr, from, to); // 取 [from,to);to 可大于 length(超出部分补默认值)

// 6. 比较 / 哈希
boolean ok = Arrays.equals(a, b); // a/b: 两个同类型数组,比内容(别用a.equals(b),那是 ==)
boolean ok = Arrays.deepEquals(m1, m2); // 多维版
int h = Arrays.hashCode(arr); // 基于内容的哈希
int h = Arrays.deepHashCode(mat); // 多维版
int p = Arrays.mismatch(a, b); // 首个不同位置,完全相同返回 -1
int r = Arrays.compare(a, b); // 字典序比较,返回正/0/负

// 7. 转换 / 批量赋值
List<Integer> list = Arrays.asList(1, 2, 3); // 元素直接当可变参数传,个数不限
Stream<Integer> st = Arrays.stream(arr); // 基本类型有IntStream/LongStream/DoubleStream 重载
Arrays.setAll(arr, i -> i * i); // lambda 的 i 是下标,返回值赋给arr[i]
Arrays.parallelSetAll(arr, i -> f(i)); // 并行版

lambda表达式

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
/* 条件:
目标类型必须是函数式接口:接口里有且仅有一个抽象方法(默认方法 default、静态方法、Object 中重写的方法如 equals 都不算)。
参数列表、返回值、抛出的异常必须和那个抽象方法一致。
不能独立存在:必须出现在“能推断出目标类型”的地方——赋值、传参、返回、强转。写成 var f = () -> {}; 或 Object o = () -> {}; 都会编译失败(目标类型不明确)。
*/
@FunctionalInterface // ① 注解:编译期校验,不是必需但强烈建议加
interface IntCalc {
int apply(int a, int b); // 唯一的抽象方法(SAM)
default void log() {} // default 方法不算,不影响"函数式"身份
boolean equals(Object o); // 继承自 Object 的不算
}
IntCalc add = (a, b) -> a + b; // ② 赋值处可推断目标类型
list.sort((x, y) -> y - x); // ③ 传参处可推断
Arrays.sort(a, (Integer o1, Integer o2) -> { return o1-o2; });
Arrays.sort(a, (o1, o2) -> o1-o2);
/*
lambda表达式遍历:
default void forEach(Consumer<? super T> action):
*/
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
//2.利用匿名内部类的形式
/* coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
// lambda表达式
coll.forEach(s -> System.out.println(s));