公司要新做一个医疗行业的业务,经过业务端和产品端的评估该业务与公司已有的产品线关联不大,用户后续也不想在老系统那台老爷车上继续使用,话说老系统到现在差不多10年了,中间经历过的前后端开发者形形色色,维护者换了一轮又一轮,系统已经日渐臃肿,页面UI也不大美观,作为技术研发人员也表示能够理解。而且从技术的角度考虑,新系统中除了要研发的新业务外,它所依赖的基础模块与传统的权限管理模式也大不相同,强行在原系统上研发的话,基础模块的重新搭建也是必不可少,到时候难免把原系统改的面目全非。经过综合考虑觉得重新搭建一套系统,前后端应用均从0开始研发。
新搭建的项目那java8肯定是不能再用了,技术人员也要跟上时代进步的步伐,果断安排上了springboot3和java17。说来也惭愧,我虽然是研发的老司机了,可新版本的jdk还没有在项目中实际使用过,这次研发新项目方才真正实践其中的一些新特性。
Records(数据类)
Records
是 Java 14 中作为预览功能引入,并在 Java 16 中正式成为永久特性的新功能。它的主要目的是简化不可变数据载体类(Data Carrier Class)的创建。这类类通常只包含数据字段,并提供访问这些字段的方法,而没有复杂的行为。
在 Records
出现之前,我们通常需要手动编写一个 POJO(Plain Old Java Object)类,包含私有字段、构造函数、getter
方法、equals()
、hashCode()
和 toString()
方法。这不仅繁琐,而且容易出错。Records
正是为了解决这个问题而设计的。
1. 什么是 Record?
Record
是一种特殊的类,它代表其状态是其唯一有意义的特征的不可变数据。它自动为你生成:
- 私有、
final
的字段:对应于record
头部中声明的组件。 - 公共构造函数:参数与
record
组件的顺序和类型相同,用于初始化字段。 - 公共的
accessor
方法(访问器):对于每个组件x
,会生成一个名为x()
的方法(注意:不是getX()
),返回该字段的值。 equals(Object o)
方法:基于所有字段的值进行比较。hashCode()
方法:基于所有字段的值生成哈希码。toString()
方法:生成包含类名和所有字段名及其值的字符串表示。
2. 基本语法
public record Person(String name, int age) {// record body (可选)
}
这个简单的声明等价于以下传统类:
// 等价的传统写法
public final class Person {private final String name;private final int age;public Person(String name, int age) {this.name = name;this.age = age;}public String name() { // 注意:是 name() 而不是 getName()return name;}public int age() { // 注意:是 age() 而不是 getAge()return age;}// 自动生成的 equals, hashCode, toString...// ... (代码省略)
}
3. 使用 Record
public class RecordUseDemo {public static void main(String[] args) {// 创建 record 实例Person person = new Person("Alice", 30);// 访问字段 (使用 accessor 方法)System.out.println(person.name()); // 输出: AliceSystem.out.println(person.age()); // 输出: 30// toString()System.out.println(person); // 输出: Person[name=Alice, age=30]// equals() 和 hashCode()Person person2 = new Person("Alice", 30);System.out.println(person.equals(person2)); // 输出: trueSystem.out.println(person.hashCode() == person2.hashCode()); // 输出: true}
}
4. Record 的特性与限制
- 隐式
final
:Record
类是隐式final
的,不能被继承。 - 隐式
public
:Record
类和其组件(字段)是隐式public
的。 - 不可变性:所有字段都是
private final
的,确保了Record
实例的不可变性。 - 无继承:
Record
不能extends
其他类(因为它隐式继承了java.lang.Record
)。 - 只能实现接口:
Record
可以implements
接口。 - 紧凑的构造函数:可以编写“紧凑构造函数”来对参数进行验证或预处理,但不能声明自己的字段。
public record Person(String name, int age) {// 紧凑构造函数 - 用于验证public Person {if (age < 0) {throw new IllegalArgumentException("Age cannot be negative");}// 注意:这里不能写 this.name = name; 或 this.age = age;// 字段的赋值由编译器自动完成// 你只能进行验证或修改组件值(通过 this(...) 调用其他构造函数,但 record 通常只有一个构造函数)// 或者调用 super(...),但 record 没有显式父类构造函数可调用。}// 你也可以添加静态方法或实例方法public boolean isAdult() {return age >= 18;}
}
- 可以添加方法:可以在
Record
中添加静态方法、实例方法、甚至getter
/setter
(尽管setter
会破坏不可变性,不推荐)。
public record Point(int x, int y) {// 静态方法public static Point origin() {return new Point(0, 0);}// 实例方法public double distanceToOrigin() {return Math.sqrt(x * x + y * y);}// 可以重写自动生成的方法@Overridepublic String toString() {return "Point(" + x + ", " + y + ")";}
}
实践示例
在数据层的Maaper中定义数据类User, 用于查询接口的方法返回对象。
public interface MessageMapper {/*** 用户对象* @param id* @param name*/record User(String id, String name) {}/*** 获取发信人* @param messageId* @return*/User getSendUser(Long messageId);
}
Text Blocks(多行字符串)
使用三重引号("""
)定义多行字符串,避免转义字符。简便了大段的文本处理效率,不用再检查字符串“+”拼接中的引号是否对称等特殊字符字符转义问题啦。
示例:
// java8 之前的写法String s1 = "{\"name\":\"John\",\"age\":30}";// java 14 引入的写法String s2= """{"name": "John","age": 30}""";
应用场景:JSON/XML配置、SQL语句拼接,提升可读性。
Switch表达式
支持模式匹配和表达式返回值,替代传统switch
语句。真是三目运算符的好帮手,写过三目运算符的都知道,有时候仅仅是多一个值选项就不得不转换为各种if else的嵌套写法,可以这样使用switch之后,写法真的简便很多。
示例:
int days=0;//java8 之前的写法switch (month) {case "JANUARY":case "MARCH":case "MAY":case "JULY":case "AUGUST":case "OCTOBER":case "DECEMBER":days = 31;break;case "APRIL":case "JUNE":case "SEPTEMBER":case "NOVEMBER":days = 30;break;case "FEBRUARY":days = 28;break;default:System.out.println("Invalid month.");break;}//java14之后的写法days = switch (month) {case "JANUARY", "MARCH", "MAY", "JULY", "AUGUST", "OCTOBER", "DECEMBER" -> 31;case "APRIL", "JUNE", "SEPTEMBER", "NOVEMBER" -> 30;case "FEBRUARY" -> 28;default -> throw new IllegalArgumentException("Invalid month: " + month);};
应用场景:状态机处理(如订单状态流转)。
以上就是我这边的使用过程和经历啦,后面项目中有其他特性应用再行补充和说明了。关于本文的内容和个人的使用经历,也欢迎大家在评论区留言和交流。