Record类:简洁的数据载体(Java代码实战)
在Java的世界里,数据类是我们经常会用到的一种类,它主要用于存储数据。传统的数据类往往需要编写大量的样板代码,比如构造函数、getter和setter方法、equals()、hashCode()以及toString()方法等。而JDK 17中引入的Record类,就像是一把神奇的钥匙,为我们打开了简洁编写数据类的大门。今天,我们就来深入学习Record类,看看它是如何作为简洁的数据载体发挥作用的。
Record类的定义和使用
什么是Record类
Record类是Java 16中引入的一种特殊类,在JDK 17中得到了进一步的完善和应用。简单来说,Record类是一种不可变的数据类,它可以自动生成构造函数、getter方法、equals()、hashCode()和toString()方法,大大减少了我们编写样板代码的工作量。
用大白话来讲,就好比我们以前要盖一座房子,需要自己一块砖一块砖地垒,还要自己安装门窗、铺设地板等,非常麻烦。而现在有了Record类,就像是有了一个预制房屋,很多结构和设施都已经帮我们做好了,我们只需要稍微布置一下就可以入住了。
定义Record类
下面我们通过一个简单的例子来看看如何定义一个Record类。假设我们要定义一个表示学生的类,包含姓名和年龄两个属性。
// 定义一个Record类
record Student(String name, int age) {
}
在这个例子中,我们使用record关键字定义了一个名为Student的Record类,括号内的String name和int age是这个Record类的组件(类似于普通类的成员变量)。
使用Record类
定义好Record类后,我们就可以使用它了。下面是一个使用Student类的示例:
public class RecordExample {
public static void main(String[] args) {
// 创建一个Student对象
Student student = new Student("Alice", 20);
// 获取学生的姓名和年龄
String name = student.name();
int age = student.age();
System.out.println("Name: " + name + ", Age: " + age);
System.out.println(student);
}
}
在这个示例中,我们创建了一个Student对象,并通过自动生成的访问器方法(name()和age())获取了学生的姓名和年龄。同时,我们还直接打印了student对象,这时候会调用自动生成的toString()方法,输出对象的信息。
Record类的构造和使用
构造Record类
Record类的构造函数是自动生成的,它会接收所有组件作为参数。我们也可以自定义构造函数,但需要注意的是,自定义构造函数必须调用自动生成的构造函数(也称为规范构造函数)。
下面是一个自定义构造函数的示例:
record Student(String name, int age) {
// 自定义构造函数
public Student {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
// 调用规范构造函数
this(name, age);
}
}
在这个示例中,我们自定义了一个构造函数,用于检查年龄是否为负数。如果年龄为负数,会抛出一个异常。
使用Record类进行数据传递
Record类非常适合作为数据载体,用于在不同的模块或方法之间传递数据。下面是一个简单的示例:
public class RecordDataTransfer {
public static void main(String[] args) {
Student student = new Student("Bob", 22);
printStudentInfo(student);
}
public static void printStudentInfo(Student student) {
System.out.println("Student Info: " + student);
}
}
在这个示例中,我们将Student对象作为参数传递给printStudentInfo方法,用于打印学生的信息。
Record类与普通类的区别
代码简洁性
普通类需要手动编写构造函数、getter和setter方法、equals()、hashCode()和toString()方法等,代码量比较大。而Record类只需要定义组件,这些方法都会自动生成,代码非常简洁。
下面是一个普通类的示例:
public class NormalStudent {
private final String name;
private final int age;
public NormalStudent(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NormalStudent that = (NormalStudent) o;
return age == that.age && name.equals(that.name);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, age);
}
@Override
public String toString() {
return "NormalStudent{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
可以看到,普通类的代码量明显比Record类多很多。
不可变性
Record类是不可变的,它的组件一旦初始化就不能再修改。而普通类可以通过setter方法修改成员变量的值。
自动生成方法
Record类会自动生成构造函数、getter方法、equals()、hashCode()和toString()方法,而普通类需要手动编写这些方法。
Record类在序列化和反序列化中的问题及解决方法
序列化和反序列化的概念
序列化是指将对象转换为字节流的过程,反序列化则是将字节流转换为对象的过程。在Java中,对象要想实现序列化和反序列化,需要实现Serializable接口。
Record类的序列化和反序列化
Record类默认是可序列化的,因为它隐式地实现了Serializable接口。下面是一个Record类序列化和反序列化的示例:
import java.io.*;
record Student(String name, int age) implements Serializable {
}
public class RecordSerialization {
public static void main(String[] args) {
Student student = new Student("Charlie", 22);
try {
// 序列化
FileOutputStream fileOut = new FileOutputStream("student.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(student);
out.close();
fileOut.close();
// 反序列化
FileInputStream fileIn = new FileInputStream("student.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
Student deserializedStudent = (Student) in.readObject();
in.close();
fileIn.close();
System.out.println("Deserialized Student: " + deserializedStudent);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在这个示例中,我们将Student对象序列化到文件中,然后再从文件中反序列化出对象。
解决可能出现的问题
虽然Record类默认是可序列化的,但在某些情况下,可能会出现一些问题。比如,当Record类的组件包含不可序列化的对象时,就会抛出NotSerializableException异常。
为了解决这个问题,我们可以将不可序列化的组件标记为transient,这样在序列化时会忽略这些组件。
import java.io.*;
record Student(String name, int age, transient Object nonSerializableObject) implements Serializable {
}
在这个示例中,nonSerializableObject被标记为transient,在序列化时会被忽略。
总结
通过学习Record类,我们掌握了一种简洁编写数据类的方法。Record类可以自动生成构造函数、getter方法、equals()、hashCode()和toString()方法,大大减少了样板代码的编写。同时,Record类是不可变的,非常适合作为数据载体。在序列化和反序列化方面,Record类默认是可序列化的,但需要注意处理不可序列化的组件。
掌握了Record类的内容后,下一节我们将深入学习JDK 17的其他新语法特性,进一步完善对本章JDK 17新语法特性实战主题的认知。
🍃 程序员JDK17修炼手册系列专栏导航
建议按系列顺序阅读,从基础到进阶逐步掌握JDK核心能力,避免遗漏关键知识点~
- 🔖 专栏目录:JDK17新特性
系列文章衔接
-
🔖 JDK5新特性
-
🔖 JDK8新特性
-
🔖 JDK11新特性
-
🔖 JDK21新特性
-
🍃 博客概览:《程序员技术成长导航,专栏汇总》
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)