Java 14相关
简述
温馨提示: 目前仅IDEA 2020.1 EAP及以上版本支持Java 14中所有新增功能.因此请使用最新版本(目前链接到2020.1 EAP版本,发布正式版之后,可在稳定版中下载.)!~
Java 14增加了一个Record的预览功能.预览:在后续JDK版本中可能有变化,也可能成为永久功能.
Record和enum枚举类似,只是Record是半不可变类.在很多地方也可以将Record当做class一样使用.
特点简述:
- record没有set,只能通过构造函数初始化;
- record编译后会生成一个最终类(final class修饰),并且无法继承任何类(因为生成类的时候,系统会自动继承java.lang.Record类),但是可以实现接口;
- 系统会自动生成: toString(),hashCode(),equals(),属性名的方法(比如属性是name,则会生成name()方法,生成的方法不带get),不会生成set方法.
定义一个简单的Record
定义一个User的record:
1 record User(String name, int age){ }
中间可以不写任何内容.
调用Record
可以像调用类一样,调用Record,如下:
1
2
3
4
5
6
7 var dur=new User("tea2",20);
var acs=new User("tea2",20);
System.out.println(dur.name());
System.out.println(dur.age());
System.out.println(dur.equals(acs));
System.out.println(acs.age());
System.out.println(acs.name());
这里的dur和acs只能通过构造函数初始化属性.系统会自动生成equals,可以直接比较两个record是否一致.
在Record中添加字段
在Record中,可以添加静态字段或者静态实例,静态方法:
1
2
3
4
5
6
7
8
9 record User(String name, int age){
public User {
callNumber++;
}
private static int callNumber;
static int getCallNumber(){
return callNumber;
}
}
调用:
1
2
3 var dur=new User("tea2",20);
var acs=new User("tea2",20);
System.out.println(User.getCallNumber());
Record的构造函数
构造函数可以写成两种方式.同时在构造函数中,进行各种验证操作.
在IDEA中可以通过快捷方式进行转换.如下图:
Java 14 Record简化构造函数转换为传统构造函数:
Java 14 Record传统构造函数转换为简化构造函数:
传统构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 record User(String name, int age){
public User(String name, int age) {
// 进行各种验证操作.
if(age<20){
throw new IllegalArgumentException("age小于20.");
}
callNumber++;
this.name = name;
this.age = age;
}
private static int callNumber;
static int getCallNumber(){
return callNumber;
}
}
简化构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13 record User(String name, int age){
public User {
// 进行验证操作.
if(age<20){
throw new IllegalArgumentException("age小于20.");
}
callNumber++;
}
private static int callNumber;
static int getCallNumber(){
return callNumber;
}
}
在Record中使用泛型
可以这样定义泛型:
1
2 record MobilePhone(T phone,double price){
}
然后定义一个Record,定义一个普通类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 record HuaWei(){
private static final String name="Huawei";
private static final String model="mate 20 pro";
public String name(){
return name;
}
public String model(){
return model;
}
}
class XiaoMi{
private static final String name="Xiaomi";
private static final String model="9";
public String name(){
return name;
}
public String model(){
return model;
}
}
调用:
1
2
3
4
5
6 var huaWeiMobilePhone=new MobilePhone(new HuaWei(),6000);
var xiaomiMobilePhone=new MobilePhone(new XiaoMi(),6100);
System.out.println(huaWeiMobilePhone.phone().name());
System.out.println(xiaomiMobilePhone.phone().name());
System.out.println(huaWeiMobilePhone.toString());
System.out.println(xiaomiMobilePhone.toString());
输出结果:
Huawei Xiaomi MobilePhone[phone=HuaWei[], price=6000.0] MobilePhone[phone=cn.bckf.java14demo.XiaoMi@25f38edc, price=6100.0]
可以看到,普通类和Record只在toString()的实现上有些差别.
Record文件读写
定义Record
1
2 record User(String name, int age) implements Serializable {
}
将Record写入文件
1
2
3
4
5
6
7 static void write(User user, String filePath) {
try (var oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(filePath)))) {
oos.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
从文件中读取Record
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 static User read(String filePath) {
User user = null;
try (var oos = new ObjectInputStream(Files.newInputStream(Paths.get(filePath)))) {
// 此处使用了instanceof模式匹配.该特性为JDK 14新增加.
if (oos.readObject() instanceof User user1) {
user = user1;
}
/*
上面的语句也可以写成:
user= (User) oos.readObject();
*/
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return user;
}
读写文件完整代码
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 import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
record User(String name, int age) implements Serializable {
}
public class Main {
static void write(User user, String filePath) {
try (var oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(filePath)))) {
oos.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
static User read(String filePath) {
User user = null;
try (var oos = new ObjectInputStream(Files.newInputStream(Paths.get(filePath)))) {
// 此处使用了instanceof模式匹配.该特性为JDK 14新增加.
if (oos.readObject() instanceof User user1) {
user = user1;
}
/*
上面的语句也可以写成:
user= (User) oos.readObject();
*/
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return user;
}
public static void main(String[] args) {
var user = new User("测试用户", 20);
var filePath = "Java14Record";
write(user, filePath);
var readUser = read(filePath);
System.out.println(readUser.toString());
}
}
Record不是关键字
目前发布的版本中,record和var一样是受限制的标识符,并非关键字.所以record可以像下面这样使用:
1
2 var record=100;
void record(){}
但是不能用于类名使用,下面的代码会提示编译错误(‘record’是受限制的标识符,不能用于类型声明):
1 class record{}
编译后的Record
IDEA提供了一个反编译的功能,位置如下:
编译之后的类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 // IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package cn.bckf.java14demo;
final class User extends java.lang.Record implements java.io.Serializable {
private final java.lang.String name;
private final int age;
public User(java.lang.String name, int age) { /* compiled code */ }
public java.lang.String toString() { /* compiled code */ }
public final int hashCode() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public java.lang.String name() { /* compiled code */ }
public int age() { /* compiled code */ }
}
会发现,编译之后,自动生成了toString(),hashCode(),equals(),和属性的获取方法(没有get前缀),同时,属性被private final修饰.
好了,可以开始愉快的使用Record了.☺