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类recordStudent(Stringname,intage){}在这个例子中我们使用record关键字定义了一个名为Student的Record类括号内的String name和int age是这个Record类的组件类似于普通类的成员变量。使用Record类定义好Record类后我们就可以使用它了。下面是一个使用Student类的示例publicclassRecordExample{publicstaticvoidmain(String[]args){// 创建一个Student对象StudentstudentnewStudent(Alice,20);// 获取学生的姓名和年龄Stringnamestudent.name();intagestudent.age();System.out.println(Name: name, Age: age);System.out.println(student);}}在这个示例中我们创建了一个Student对象并通过自动生成的访问器方法name()和age()获取了学生的姓名和年龄。同时我们还直接打印了student对象这时候会调用自动生成的toString()方法输出对象的信息。Record类的构造和使用构造Record类Record类的构造函数是自动生成的它会接收所有组件作为参数。我们也可以自定义构造函数但需要注意的是自定义构造函数必须调用自动生成的构造函数也称为规范构造函数。下面是一个自定义构造函数的示例recordStudent(Stringname,intage){// 自定义构造函数publicStudent{if(age0){thrownewIllegalArgumentException(Age cannot be negative);}// 调用规范构造函数this(name,age);}}在这个示例中我们自定义了一个构造函数用于检查年龄是否为负数。如果年龄为负数会抛出一个异常。使用Record类进行数据传递Record类非常适合作为数据载体用于在不同的模块或方法之间传递数据。下面是一个简单的示例publicclassRecordDataTransfer{publicstaticvoidmain(String[]args){StudentstudentnewStudent(Bob,22);printStudentInfo(student);}publicstaticvoidprintStudentInfo(Studentstudent){System.out.println(Student Info: student);}}在这个示例中我们将Student对象作为参数传递给printStudentInfo方法用于打印学生的信息。Record类与普通类的区别代码简洁性普通类需要手动编写构造函数、getter和setter方法、equals()、hashCode()和toString()方法等代码量比较大。而Record类只需要定义组件这些方法都会自动生成代码非常简洁。下面是一个普通类的示例publicclassNormalStudent{privatefinalStringname;privatefinalintage;publicNormalStudent(Stringname,intage){this.namename;this.ageage;}publicStringgetName(){returnname;}publicintgetAge(){returnage;}Overridepublicbooleanequals(Objecto){if(thiso)returntrue;if(onull||getClass()!o.getClass())returnfalse;NormalStudentthat(NormalStudent)o;returnagethat.agename.equals(that.name);}OverridepublicinthashCode(){returnjava.util.Objects.hash(name,age);}OverridepublicStringtoString(){returnNormalStudent{namename\, ageage};}}可以看到普通类的代码量明显比Record类多很多。不可变性Record类是不可变的它的组件一旦初始化就不能再修改。而普通类可以通过setter方法修改成员变量的值。自动生成方法Record类会自动生成构造函数、getter方法、equals()、hashCode()和toString()方法而普通类需要手动编写这些方法。Record类在序列化和反序列化中的问题及解决方法序列化和反序列化的概念序列化是指将对象转换为字节流的过程反序列化则是将字节流转换为对象的过程。在Java中对象要想实现序列化和反序列化需要实现Serializable接口。Record类的序列化和反序列化Record类默认是可序列化的因为它隐式地实现了Serializable接口。下面是一个Record类序列化和反序列化的示例importjava.io.*;recordStudent(Stringname,intage)implementsSerializable{}publicclassRecordSerialization{publicstaticvoidmain(String[]args){StudentstudentnewStudent(Charlie,22);try{// 序列化FileOutputStreamfileOutnewFileOutputStream(student.ser);ObjectOutputStreamoutnewObjectOutputStream(fileOut);out.writeObject(student);out.close();fileOut.close();// 反序列化FileInputStreamfileInnewFileInputStream(student.ser);ObjectInputStreaminnewObjectInputStream(fileIn);StudentdeserializedStudent(Student)in.readObject();in.close();fileIn.close();System.out.println(Deserialized Student: deserializedStudent);}catch(IOException|ClassNotFoundExceptione){e.printStackTrace();}}}在这个示例中我们将Student对象序列化到文件中然后再从文件中反序列化出对象。解决可能出现的问题虽然Record类默认是可序列化的但在某些情况下可能会出现一些问题。比如当Record类的组件包含不可序列化的对象时就会抛出NotSerializableException异常。为了解决这个问题我们可以将不可序列化的组件标记为transient这样在序列化时会忽略这些组件。importjava.io.*;recordStudent(Stringname,intage,transientObjectnonSerializableObject)implementsSerializable{}在这个示例中nonSerializableObject被标记为transient在序列化时会被忽略。总结通过学习Record类我们掌握了一种简洁编写数据类的方法。Record类可以自动生成构造函数、getter方法、equals()、hashCode()和toString()方法大大减少了样板代码的编写。同时Record类是不可变的非常适合作为数据载体。在序列化和反序列化方面Record类默认是可序列化的但需要注意处理不可序列化的组件。掌握了Record类的内容后下一节我们将深入学习JDK 17的其他新语法特性进一步完善对本章JDK 17新语法特性实战主题的认知。 程序员JDK17修炼手册系列专栏导航建议按系列顺序阅读从基础到进阶逐步掌握JDK核心能力避免遗漏关键知识点 专栏目录JDK17新特性系列文章衔接 JDK5新特性 JDK8新特性 JDK11新特性 JDK21新特性 博客概览《程序员技术成长导航专栏汇总》