Java17

项目快速集成Java 17

image.png
IDEA 集成 Java 17
亚马逊的Corretto JDK 17 、Zulu JDK 17 都已经加入了豪华午餐。
IDEA默认解压到Windows当前用户文件夹路径下(这里的是C:\Users\fcsca.jdks),之所以解压到.jdks下是因为IDEA的下载目标文件夹就是这个文件夹,方便IDEA自动检出。
image.png
解压完成的效果
这里不需要重新配置Java环境变量,都是项目级别的Java版本控制,不会对其它项目造成影响。
然后新建一个Maven项目(也可以是普通项目或者Gradle项目),这个时候还需要配置项目的语言级别等。

语言级别

调整JDK的语言级别为Java 17 ,在IDEA下按快捷键 Ctrl+Alt+Shift+S 呼出下面的对话框并将Language Level修改为17。
image.png
image.png
修改项目 JDK Level

字节码版本

编译器的字节码版本也需要调整为17。在IDEA中按下快捷键 Ctrl+Alt+S 在图示中的位置进行修改。
image.png
修改编译器的字节码版本

Record Class

搞定了环境配置后,开始试一试一个最直观的、也相当有用的语法糖Record。
准确地说这不属于Java 17的新特性,最早在Java 14 中出现,在Java 16中转为正式特性。不过作为LTS版本,这依然是很重要的一个概念。
直观一些,一个数据类传统的写法是:

  1. public class MyRecord {
  2. private final String username;
  3. private final Integer age;
  4. public MyRecord(String username, Integer age) {
  5. this.username = username;
  6. this.age = age;
  7. }
  8. public String username() {
  9. return username;
  10. }
  11. public Integer age() {
  12. return age;
  13. }
  14. @Override
  15. public boolean equals(Object o) {
  16. if (this == o) return true;
  17. if (o == null || getClass() != o.getClass()) return false;
  18. MyRecord oldRecord = (MyRecord) o;
  19. return Objects.equals(username, oldRecord.username)
  20. && Objects.equals(age, oldRecord.age);
  21. }
  22. @Override
  23. public int hashCode() {
  24. return Objects.hash(username, age);
  25. }
  26. @Override
  27. public String toString() {
  28. return "MyRecord[" +
  29. "username='" + username + '\'' +
  30. ", age=" + age +
  31. ']';
  32. }
  33. }

Record就可以简化为:

  1. public record MyRecord(String username,Integer age) {
  2. }

这样大大减少了一些模板代码,让逻辑更加清晰简单。一定意义上代替了Lombok。

Record 是不可变的

Record被用来设计传输不可变的数据。从上面的例子可以看到,一个Record类被初始化后里面的属性是不能改变的,没有Setter方法而是通过全参数构造来初始化数据,天然线程安全。

Record的超类

所有用Record关键字声明的类都是java.lang.Record的子类,这一点有点像枚举。

  1. public abstract class Record {
  2. protected Record() {}
  3. @Override
  4. public abstract boolean equals(Object obj);
  5. @Override
  6. public abstract int hashCode();
  7. @Override
  8. public abstract String toString();
  9. }

从这里也可以看出所有Record的实现都覆写了equalshashCodetoString三个方法。
如何判断一个类是Record类?
传统方法:

  1. Record.class.isAssignableFrom(MyRecord.class)

JDK提供了一个新的方法来解决这个问题:

  1. MyRecord.class.isRecord()

值得一提的是Class类还提供了getRecordComponents来获取Record类的成员属性信息。

  1. RecordComponent[] recordComponents = MyRecord.class.getRecordComponents();

Record无法使用extends关键字

由于Record类的唯一的隐式超类是java.lang.Record,Java不支持多继承,使用 extends 显式定义会导致编译错误。

无法定义额外的成员变量

Record类的成员变量只能通过构造声明。所以下面这种写法是错误的:

  1. public record MyRecord(String username,Integer age) {
  2. privite String gender;
  3. }

但是可以在Record类中定义静态变量。

定义方法时需要小心

定义方法比较开放,但是请确保定义的方法不会破坏Record不可变的含义。不推荐定义Setter方法。
另外注意Record类的Getter方法不是setXXXX格式的。

使用注解

唯一需要注意的是,在Record类的成员变量上使用注解可能会作用的Getter方法上。就像这样:

  1. public record MyRecord(@Deprecated String username,Integer age) {
  2. }

编译后:

  1. public record MyRecord(String username, Integer age) {
  2. public MyRecord(@Deprecated String username, Integer age) {
  3. this.username = username;
  4. this.age = age;
  5. }
  6. public String getUsername() {
  7. return this.username;
  8. }
  9. /** @deprecated */
  10. @Deprecated
  11. public String username() {
  12. return this.username;
  13. }
  14. public Integer age() {
  15. return this.age;
  16. }
  17. }

具体的作用域需要根据注解上的@Target元注解的定义域来判定。