JDK8新特性

Lambda

函数式编程思想概述

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“数据做操作“面向对象思想强调“必须通过对象的形式来做事情”

函数式思想则尽量忽略面向对像的复杂语法:“强调做什么,而不是以什么形式去做”

而我们要学习的Lambda表达式就是函数式思想的体现

体验lambda表达式

需求:启动一个线程,在控制台输出一句话:多线程程序启动了

方式1

  • 定义一个类MyRunnable:实现Runnable接口,重写run() 方法

  • 创建MyRunnable类的对象

  • 创建Thread类的对象,把MyRunnable的对象作为构造参数传递

  • 启动线程

方式2

  • 匿名内部类的方式改进

方式3

  • 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
33
34
//1.创建MyRunnable类
package demo_01;

public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("多线程启动了");
}
}

//2.实现类
package demo_01;
//需求:启动一个线程,在控制台输出一句话:多线程程序启动了
public class LambdaDemo {
public static void main(String[] args) {
//实现类的方式实现需求
/* MyRunnable my = new MyRunnable();
Thread th = new Thread(my);
th.start();*/

//匿名内部类的方式改进
/*new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程启动了");
}
}).start();*/

//Lambda表达式的方式改进
new Thread(() -> {
System.out.println("多线程启动");
}).start();
}
}

Lambda表达式的标准格式

匿名内部类中重写run() 方法的代码分析:

  • 方法形式参数为空,说明调用方法时不需要传递参数

  • 方法返回值类型为void,说明方法执行没有结果返回

  • 方法体中的内容,是我们具体要做的事情

1
2
3
4
5
6
7
//匿名内部类的方式改进
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程启动了");
}
}).start();

Lambda表达式的代码分析:

  • () :里面没有内容,可以看成是方法形式参数为空
  • -> :用箭头指向后面要做的事情
  • {}:包含一段代码,我们称之为代码块,可以看成是方法体中的内容

组成Lambda表达式的三要素:形式参数,箭头,代码块

1
2
3
4
//Lambda表达式的方式改进
new Thread(() -> {
System.out.println("多线程启动");
}).start();

Lambda表达式的格式:

  • 格式:(形式参数)-> {代码块}

  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可

  • ->:由英文中画线和大于符号组成,固定写法。代表指向动作

  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

Lambda表达式的使用

Lambda表达式的使用前提:

  • 有一个接口

  • 接口中有且仅有一个抽象方法

练习1:

  • 定义一个接口(Eatable),里面定义一个抽象方法:void eat();

  • 定义一个测试类(EatableDemo),在测试类中提供两个方法:

    • 一个方法是:useEatable(Eatable e)

    • 一个方法是主方法,在主方法中调用useEatable方法

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
//1.创建接口
package demo_02;

public interface Eatable {
void eat();
}

//2.接口实现类
package demo_02;

public class EatableLmpl implements Eatable{
@Override
public void eat() {
System.out.println("吃水果");
}
}

//3.测试类
package demo_02;
/*
- 定义一个接口(Eatable),里面定义一个抽象方法:void eat();
- 定义一个测试类(EatableDemo),在测试类中提供两个方法:
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
*/
public class EatableDemo {
public static void main(String[] args) {
//接口实现类方法
Eatable el = new EatableLmpl();
useEatable(el);

//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("吃水果");
}
});

//lambda表达式
useEatable(()->{
System.out.println("吃水果");
});

}
public static void useEatable(Eatable e){
e.eat();
}
}

练习2:

  • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s):

  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法

    • 一个方法是:useFlyable(Flyable f)

    • 一个方法是主方法,在主方法中调用useFlyable方法

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
//1.创建Flyable接口
package demo_03;

public interface Flyable {
void fly(String s);
}

//测试类
package demo_03;
/*
- 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s):
- 定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
*/
public class FlyableDemo {
public static void main(String[] args) {
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("万物复苏");
}
});

//lanbada表达式
useFlyable((s -> {
System.out.println("lambada引用");
}));

}
public static void useFlyable(Flyable f){
f.fly("出暖花开,生机勃勃");
}
}

练习3:

  • 定义一个接口(Addable),里面定义一个抽象方法:int add(intx, inty);

  • 定义一个测试类(AddableDemo),在测试类中提供两个方法:

    • 一个方法是:useAddable(Addable a
    • 一个方法是主方法,在主方法中调用useAddable方法
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
//1.创建接口
package demo_04;

public interface AddAble {
int add(int x,int y);
}

//2.测试类
package demo_04;
/*
- 定义一个接口(Addable),里面定义一个抽象方法:int add(intx, inty);
- 定义一个测试类(AddableDemo),在测试类中提供两个方法:
一个方法是:useAddable(Addable a
一个方法是主方法,在主方法中调用useAddable方法
*/
public class AddAbleDemo {
public static void main(String[] args) {
//调用useAddable方法
useAddable(((x, y) -> {
return x + y;
}));
}
public static void useAddable(AddAble a){
int sum = a.add(10,20);
System.out.println(sum);
}
}

Lambda表达式的省略模式

省略规则:

  • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个

  • 如果参数有且仅有一个,那么小括号可以省略

  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return

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
//1.定义Addable接口
package demo_05;

public interface Addable {
int add(int x, int y);
}

//2.创建Flyanle接口
package demo_05;

public interface Flyable {
void fly(String s);
}

//3.测试类
package demo_05;

import demo_03.Flyable;
import demo_04.AddAble;

public class LambdaDemo {
public static void main(String[] args) {
//参数类型可以省略
useAddable((x,y)->{
return x + y;
});

useFlyable((s) -> {
System.out.println(s);
});

//如果参数有且仅有一个,那么小括号也可以省略
useFlyable(s -> System.out.println(s));

//如果代码语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));

//如果代码语句只有一条,可以省略大括号和分号,如果有return,也可以省略掉
useAddable((x,y)-> x + y);
}
public static void useFlyable(Flyable f){
f.fly("春暖花开,万物复苏");
}

public static void useAddable(AddAble a){
int sum = a.add(20,30);
System.out.println(sum);
}
}

Lambda表达式的注意事项

注意事项:

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
  • 必须有上下文环境,才能推导出Lambda对应的接口
  • 根据局部变量的赋值得知Lambda对应的接口:Runnable r=()->System.out.println(“Lambda表达式”);
  • 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println(“Lambda表达式”).start();

Lambda表达式和匿名内部类的区别

1.所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类

  • Lambda表达式:只能是接口

2.使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类

  • 如果接口中有多个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

3.实现原理不同

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

stream 流

什么是 Stream ? 这里的 Stream 不同于 io 中的 InputStream 和 OutputStream,Stream 位于包 java.util.stream 中, 也是 java 8 新加入的,Stream 只的是一组支持串行并行聚合操作的元素,可以理解为集合或者迭代器的增强版。

Stream 支持操作主要有 map, reduce, 排序,最大值,最小值等。

Stream 的几个特征:

  • 单次处理。一次处理结束后,当前Stream就关闭了。
  • 支持并行操作

常见的获取 Stream 的方式

  • 从集合中获取
    • Collection.stream();
    • Collection.parallelStream();
  • 静态工厂
    • Arrays.stream(array)
    • Stream.of(T …)
    • IntStream.range()

Optional 类

先说一下 Optional 类, 因为 Stream 中有些操作返回的 Optional 类,而不是对象本身。Optional 类始于 jdk 1.8,Optional 是对具体对象的简单的包装,并提供了部分操作 主要目的是防止产生空指针异常。如果以前了解过 Null Object Pattern,就很容易了解 Optional 存在的意义。

主要方法:

  • get 返回包装的对象
  • isPresent 判断是否包装了对象
  • ifPresent(Consumer) 如果存在包装对象,则对包装对象进行一定的操作

allMatch

使用给定的 Predicate 检查 Stream 中的所有元素,全部都通过检测则返回 true,否则 false 。

1
2
3
System.out.println(Stream.of(1,2,3).allMatch(n -> n >= 1));
System.out.println(Stream.of(1,2,3).allMatch(n -> n >= 3));
System.out.println(Stream.of(1,2,3).allMatch(n -> n >= 4));

anyMatch

使用给定的 Predicate 检查 Stream 中的所有元素,至少有一个通过检测则返回 true,否则 false 。

collect

collect 操作使用给定的 Collector 做 reduce 操作。

数组元素连接

1
System.out.println(Stream.of("A", "B", "C").collect(Collectors.joining(",")));

转成 List

1
List asList = Stream.of("A", "B", "C").collect(Collectors.toList());

根据城市分组

1
Map> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));

根据州和城市分组

1
2
3
Map>> peopleByStateAndCity
= personStream.collect(Collectors.groupingBy(Person::getState,
Collectors.groupingBy(Person::getCity)));

count

返回 Stream 中的元素总数。

1
System.out.println(Stream.of(1,2,3).count());

distinct

返回唯一的元素列表,类似于 数据库 sql 中的 distinct 关键字。 比较时通过 equals 方法来判定是否相同。

1
System.out.println(Stream.of(1,2,3,3).distinct().map(n -> n + "").collect(Collectors.joining(",")));

filter

使用给定的 Predicate 的筛选 Stream 元素,符合条件的留下并组成一个新的 Stream 。

1
System.out.println(Stream.of(1,2,3).filter(n -> n > 1).map(n -> n + "").collect(Collectors.joining(",")));

findAny

返回任何一个不确定的元素,通过 Optional 来包装。如果在一个固定不变的组合中,返回第一个元素。

1
System.out.println(Stream.of(1,2,3).findAny().get());

findFirst

返回第一个元素。

1
System.out.println(Stream.of(1,2,3).findFirst().get());

flatMap

适用于如果Stream中的元素还是集合,能将集合中的元素组成一个平面的集合。简单来下面的例子,Stream 是二维的,因为 Stream 的元素还是数组,经过flag处理后,变成一维的了,所有元素位于一个Stream 下了。

1
2
3
4
System.out.println(
Stream.of(new Integer[]{1,2,3}, new Integer[]{4,5,6}, new Integer[]{7,8,9,0})
.flatMap(a -> Arrays.stream(a))
.map(n -> n + "").collect(Collectors.joining(",")));

forEach

逐个元素执行 Consumer 操作。

1
Stream.of(1,2,3).forEach(n -> System.out.print(n + ","));

limit

取出指定个数的元素组成新的 Stream .

1
System.out.println(Stream.of(1,2,3).limit(2).map(n -> n + "").collect(Collectors.joining(",")));

map

map 方法的作用是依次对 Stream 中的元素进行指定的函数操作,并将按顺序将函数操作的返回值组合到一个新的 Stream 中。

下面例子将每个元素的值 +1

1
System.out.println(Stream.of(1,2,3).map(n -> n + 1).map(String::valueOf).collect(Collectors.joining(",")));

max

max 通过给定的比较器,将最大的元素取出来,返回 Optional

1
System.out.println(Stream.of(1,2,3).max((a, b) -> a - b).get());

min

min 通过给定的比较器,将最小的元素取出来,返回 Optional

1
System.out.println(Stream.of(1,2,3).min((a, b) -> a - b).get());

noneMatch

noneMatch 于 allMatch, anyMatch 类似,使用给定的 Predicate 检查 Stream 中的所有元素,全部不通过检测则返回 true,否则 false 。

1
2
System.out.println(Stream.of(1,2,3).noneMatch(n -> n > 1));
System.out.println(Stream.of(1,2,3).noneMatch(n -> n > 3 || n < 1));

reduce

reduce 的函数操作为二元操作符,一个为前面操作的结果,一个为当前元素,reduce 会逐个对 Stream 中的元素执行指定的操作,并返回最终的结果。

如求和

1
2
System.out.println(Stream.of(1,2,3).reduce(0, (a, b) -> a + b));
System.out.println(Stream.of(1,2,3).reduce((a, b) -> a + b).get());

skip

忽略给定个数的元素,返回剩下的元素组成 Stream 。

1
System.out.println(Stream.of(1,2,3).skip(1).map(n -> n + "").collect(Collectors.joining(",")));

sorted

通过给定的比较器排序,将排序后的元素的 Stream 返回。

1
2
System.out.println(Stream.of(1,2,3).sorted().map(n -> n + "").collect(Collectors.joining(",")));
System.out.println(Stream.of(1,2,3).sorted((a, b) -> b - a).map(n -> n + "").collect(Collectors.joining(",")));

方法引用

方法引用概述

  • 方法引用使用一对冒号 :: , 方法引用就是用来在一定的情况下,替换Lambda表达式

方法引用基本使用

  • 使用场景:
    • 如果一个Lambda表达式大括号中的代码和另一个方法中的代码一模一样,那么就可以使用方法引用把该方法引过来,从而替换Lambda表达式
    • 如果一个Lambda表达式大括号中的代码就是调用另一方法,那么就可以使用方法引用把该方法引过来,从而替换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
33
34
35
public class Test {

public static void show(){
System.out.println("线程执行了");
}

public static void main(String[] args) {
/*
方法引用:
概述:方法引用使用一对冒号 :: , 方法引用就是用来在一定的情况下,替换Lambda表达式
使用场景:
1.如果Lambda表达式的大括号中的代码和另一个方法的方法体一模一样,那么就可以使用方法引用把该方法
直接引过来,从而替换Lambda表达式
2.如果Lambda表达式的大括号中的代码就是调用另一个方法,那么就可以使用方法引用把该方法
直接引过来,从而替换Lambda表达式
*/
// 创建并启动线程
new Thread(()->{
System.out.println("线程执行了");
}).start();

// 发现上述的Lambda表达式大括号中的内容和Test类的show方法的方法体一模一样,符合方法引用替换Lambda表达式的场景
new Thread(Test::show).start();

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

new Thread(()->{
Test.show();
}).start();

// 发现上述的Lambda表达式大括号中的内容就是调用Test类的show方法,符合方法引用替换Lambda表达式的场景
new Thread(Test::show).start();
}
}

方法引用的分类

构造方法引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2 {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
list.add("杨紫");
list.add("迪丽热巴");
list.add("陈钰琪");

// 需求: 把集合中的元素转换为Person对象,打印输出
list.stream().map(s-> new Person(s)).forEach(s-> System.out.println(s));

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

list.stream().map(Person::new).forEach(s-> System.out.println(s));

}
}

静态方法引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2 {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
list.add("110");
list.add("111");
list.add("112");

// 需求:把集合中的元素转换为int类型,打印输出
list.stream().map(s-> Integer.parseInt(s)).forEach(s-> System.out.println(s));

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

list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));

}
}

对象成员方法引用

成员方法有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2 {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
list.add("杨紫");
list.add("迪丽热巴");
list.add("陈钰琪");

// 需求:把集合中所有元素打印输出
list.stream().forEach(s-> System.out.println(s));

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

list.stream().forEach(System.out::println);

}
}

类的成员方法引用

成员方法没有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2 {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
list.add("杨紫");
list.add("迪丽热巴");
list.add("陈钰琪");

// 需求: 把集合中的元素转换为该元素对应的字符长度,打印输出
list.stream().map(s->s.length()).forEach(System.out::println);

System.out.println("=================================");
//会默认的用参数s去调用String类中的length()方法
list.stream().map(String::length).forEach(System.out::println);

}
}

小结

1
2
3
4
5
6
7
8
9
总结:使用方法引用的步骤
1.分析要写的Lambda表达式的大括号中是否就是调用另一个方法
2.如果是,就可以使用方法引用替换,如果不是,就不能使用方法引用
3.确定引用的方法类型(构造方法,成员方法,静态方法,类的成员方法)
4.按照对应的格式去引用:
构造方法: 类名::new
成员方法(有参数): 对象名::方法名
静态方法: 类名::方法名
类的成员方法\成员方法(无参数): 类名::方法名

Base64

Base64概述

  • Base64是jdk8提出的一个新特性,可以用来进行按照一定规则编码和解码

Base64编码和解码的相关方法

  • 编码的步骤:

    • 获取编码器
    • 调用方法进行编码
  • 解码步骤:

    • 获取解码器
    • 调用方法进行解码
  • Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

    • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/
    • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
    • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用\r并跟随\n作为分割。编码输出最后没有行分割。
  • 获取编码器和解码器的方法

    1
    2
    3
    4
    5
    6
    7
    8
    static Base64.Decoder getDecoder() 基本型 base64 解码器。
    static Base64.Encoder getEncoder() 基本型 base64 编码器。

    static Base64.Decoder getMimeDecoder() Mime型 base64 解码器。
    static Base64.Encoder getMimeEncoder() Mime型 base64 编码器。

    static Base64.Decoder getUrlDecoder() Url型 base64 解码器。
    static Base64.Encoder getUrlEncoder() Url型 base64 编码器。
  • 编码和解码的方法:

    1
    2
    Encoder编码器:  encodeToString(byte[] bys)编码
    Decoder解码器: decode(String 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
public class Test1 {
public static void main(String[] args) {
// 使用基本型的编码器和解码器对数据进行编码和解码:
// 1.获取编码器
Base64.Encoder encoder = Base64.getEncoder();

// 2.对字符串进行编码
String str = "name=中国?password=123456";
String str1 = encoder.encodeToString(str.getBytes());

// 3.打印输出编码后的字符串
System.out.println("编码后的字符串:"+str1);

// 4.获取解码器
Base64.Decoder decoder = Base64.getDecoder();

// 5.对编码后的字符串进行解码
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);

// 6.打印输出解码后的字符串
System.out.println("解码后的字符串:"+str2);
}
}
  • URL
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 Test2 {
public static void main(String[] args) {

// 使用URL型的编码器和解码器对数据进行编码和解码:
// 1.获取编码器
Base64.Encoder encoder = Base64.getUrlEncoder();

// 2.对字符串进行编码
String str = "name=中国?password=123456";
String str1 = encoder.encodeToString(str.getBytes());

// 3.打印输出编码后的字符串
System.out.println("编码后的字符串:"+str1);

// 4.获取解码器
Base64.Decoder decoder = Base64.getUrlDecoder();

// 5.对编码后的字符串进行解码
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);

// 6.打印输出解码后的字符串
System.out.println("解码后的字符串:"+str2);
}
}

  • MIME
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 class Test3 {
public static void main(String[] args) {
// 使用MIME型的编码器和解码器对数据进行编码和解码:
// 1.获取编码器
Base64.Encoder encoder = Base64.getMimeEncoder();

// 2.对字符串进行编码
String str = "";
for (int i = 0; i < 100; i++) {
str += i;
}
System.out.println("编码前的字符串:"+str);

String str1 = encoder.encodeToString(str.getBytes());

// 3.打印输出编码后的字符串
System.out.println("编码后的字符串:"+str1);

// 4.获取解码器
Base64.Decoder decoder = Base64.getMimeDecoder();

// 5.对编码后的字符串进行解码
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);

// 6.打印输出解码后的字符串
System.out.println("解码后的字符串:"+str2);
}
}

StringJoiner使用

image-20220717120851789

jdk8-17新特性

Java Record

Java14 中预览的新特性叫做 Record,在 Java 中,Record 是一种特殊类型的 Java 类。可用来创建不可变类,语法简短。参考JEP 395. Jackson 2.12 支持 Record 类。

  • 任何时候创建 Java 类,都会创建大量的样板代码,我们可能做如下:
  • 每个字段的 set,get 方法
  • 公共的构造方法
  • 重写 hashCode, toString(), equals()方法

Java Record 避免上述的样板代码,如下特点:

  • 带有全部参数的构造方法
  • public 访问器
  • toString(),hashCode(),equals()
  • 无 set,get 方法。没有遵循 Bean 的命名规范
  • final 类,不能继承 Record,Record 为隐士的 final 类。除此之外与普通类一样
  • 不可变类,通过构造创建 Record
  • final 属性,不可修改
  • 不能声明实例属性,能声明 static 成员
  • IDEA 创建新的 Maven 工程 Lession01-feature

相当于Jdk编译器(语言级别)内置的Lombok(编译级别)

上手:

IDEA 新建 Class,选择类 Record

image-20230829145425880

Record 通过构造方法创建了只读的对象,能够读取每个属性,不能设置新的属性值。

Record 用于创建不可变的对象,同时减少了样板代码。

Record 对每个属性提供了 public 访问器,例如 lisi.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.zuoer.demo.model;

import java.util.Optional;

/**
* @author zuoer
* @date 2023-08-29 15:05
*/
public record Student(Integer id,String name,String email,Integer age) {
// 构造方法
// 紧凑型构造方法没有任何参数,甚至没有括号。
// 规范构造方法是以所有成员作为参数
// 定制构造方法是自定义参数个数
public Student {
System.out.println("id="+id);
if (id < 1) {
throw new RuntimeException("id小于1");
}
}
// 定制构造
public Student(Integer id,String name) {
this(id,name,null,null);
}

// 实例方法
public String concat() {
return String.format("姓名:%s,年龄:%d",this.name,this.age);
}
// 静态方法:Record 类定义静态方法,试用静态方法与普通类一样
public static String emailUpperCase(String email) {
return Optional.ofNullable(email).orElse("no email").toUpperCase();
}
}
class StudentTest {
@Test
void test01() {
Student lisi = new Student(1,"lisi","",18);
System.out.println("lisi = " + lisi);
String concat = lisi.concat();
System.out.println("concat = " + concat);
String emailUpperCase = Student.emailUpperCase("11111@qq.com");
System.out.println("emailUpperCase = " + emailUpperCase);
// 测试构造方法
Student s1 = new Student(2,"zs");
System.out.println("s1 = " + s1);
}
}

Record Lombok

Java Record 是创建不可变类且减少样板代码的好方法。Lombok 是一种减少样板代码的工具。两者有表面上的重叠部分。可能有人会说 Java Record 会代替 Lombok. 两者是有不同用途的工具。

Lombok 提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是为了方便实现 POJO 类。通过预编译代码。将代码的模板加入到 class 中。

Java Record 是语言级别的,一种语义特性,为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据载体”,用于在类和应用程序之间进行数据传输。

Record实现接口

实体类直接实现接口

1
2
3
4
5
interface Person {
String say(String word);
}
public record Student(Integer id,String name,String email,Integer age) implements Person{
}

Record作为局部对象使用

在代码块中定义并使用 Record

1
2
3
4
5
public String say(String name) {
record girl(String girlName) {} // 局部对象
girl girl = new girl("大乔");
return "我是学生:"+name+",我的女朋友是:"+girl.girlName;
}

Swich 开关表达式

Text Block 文本块

var 声明局部变量

sealed 密封类