JavaRush /Java 博客 /Random-ZH /喝咖啡休息#230。Java 中的记录是什么以及它们如何工作?

喝咖啡休息#230。Java 中的记录是什么以及它们如何工作?

已在 Random-ZH 群组中发布
来源:JavaTechOnline 本文通过示例深入介绍了 Java 中记录的概念,包括它们的语法、如何创建它们以及如何使用它们。 喝咖啡休息#230。 Java 中的记录是什么以及它们如何工作 - 1Java 中的 Records 是最伟大的功能之一,最初作为预览功能在 Java 14 中引入,最终在 Java 17 版本中出现。许多开发人员积极使用它,这帮助他们成功减少了大量的样板代码。此外,多亏了记录,您不必编写一行代码来使类不可变。

何时使用 Java 中的 Record?

如果您想在应用程序的不同层之间传递不可变数据,那么使用 Record 可能是一个不错的选择。默认情况下,Java 中的记录是不可变的,这意味着一旦创建它们,我们就无法更改它们的属性。因此,这有助于我们避免错误并提高代码的可靠性。简单来说,在Java中使用Record,我们可以自动生成类。

在 Java 中哪里可以使用 Record?

一般来说,我们可以在任何需要声明具有不可变属性和自动生成方法的简单数据容器的情况下使用记录。例如,下面是记录有用的一些用例: 数据传输对象(DTO):我们可以使用 Record 来声明包含数据的简单数据传输对象。这在不同应用层之间(例如服务层和数据库层之间)传输数据时非常有用。 配置对象:记录可用于声明包含应用程序或模块的一组配置属性的配置对象。这些对象通常具有不可变的属性,使其线程安全且易于使用。 值对象。 Record可用于声明包含一组表示特定概念或领域模型的值的值对象。 API 响应:创建 REST API 时,数据通常以 JSON 或 XML 的形式返回。在这种情况下,您可能需要定义一个表示 API 响应的简单数据结构。记录是实现此目的的理想选择,因为它们允许您定义轻量级且不可变的数据结构,并且可以轻松序列化为 JSON 或 XML。 测试数据。编写单元测试时,您通常需要创建代表特定场景的测试数据。在这种情况下,您可能需要定义一个表示测试数据的简单数据结构。记录对此非常理想,因为它们允许我们用最少的样板代码定义轻量级且不可变的数据结构。 类元组对象:记录可用于声明包含固定数量关联值的类元组对象。当从方法返回多个值或处理相关值的集合时,这非常有用。

Java 中的记录是什么?

Java中的Record是一个用来存储数据的类。它与传统的 Java 类类似,但更轻量级并且具有一些独特的功能。默认情况下,记录是不可变的,这意味着它们的状态一旦创建就无法更改。这使得它们非常适合存储不可变数据,例如配置参数或从数据库查询返回的值。它也是创建由一组字段或变量组成的自定义数据类型以及访问和修改这些字段的方法的方法。Java 中的Record通过减少开发人员必须一遍又一遍编写的样板代码量,使数据处理变得更加容易。在 Java 中创建条目的语法是:
record Record_Name(Fields....)

如何在Java中创建和使用Record并举例说明?

让我们看看如何在 Java 中以编程方式创建和使用记录。

创建记录

以编程方式创建记录与在 Java 中创建常规类不太相似。我们使用record关键字代替class。您还需要在记录名称的括号中声明数据类型的字段。下面是一个代码示例,演示了如何在 Java 中创建条目:
public record Book(String name, double price) { }
在此示例中,我们创建了一条名为Book 的记录,其中包含两个字段:namePricepublic关键字表示该条目可以在定义它的包外部访问。

使用记录

要在 Java 中使用记录,我们可以使用 new 关键字实例化它,就像我们使用常规类一样。这是一个例子:
Book book = new Book( "Core Java" , 324.25);
这里创建了一个新的Book记录实例,名称字段设置为Core Java价格字段设置为324.25。创建记录后,可以使用字段本身的名称来访问其字段。没有 get 或 set 方法。相反,字段名称成为方法名称。
String name = book.name();

double price = book.price();
在这里,我们看到从Book记录中检索到的名称价格字段 的值。除了字段之外,记录还具有一些可用于操作它们的内置方法。例如,toString()equals()hashCode()请记住,Java 记录默认情况下是不可变的,这意味着一旦创建它们,它们的状态就无法更改。 让我们看一下如何在 Java 中创建和使用记录的另一个示例。让我们举一个例子,我们需要一条记录来存储有关学生的信息。
public record Student(String name, int age, String subject) {}
这将创建一个名为Student 的记录,其中包含三个字段:姓名年龄主题。我们可以按如下方式创建此条目的实例:
Student student = new Student("John Smith", 20, "Computer Science");
然后我们可以使用字段名称获取字段值:
String name = student.name();
int age = student.age();
String subject= student.subject();

编译后的记录是什么样的?

由于Record只是一种特殊的类,编译器也会将其转换为常规类,但有一些限制和差异。当编译器在编译过程后将记录(Java 文件)转换为字节码时,生成的.class文件包含Record类的一些附加声明。例如,下面是Java 编译器 为Student条目生成的字节码:
record Student(String name, int age) {   }

写入.class文件(编译后)

如果我们使用javap工具并从命令行应用以下命令,我们还可以找到以下转换后的类。需要注意的是,Java 命令行包含javap工具,您可以使用它来查看有关类文件的字段、构造函数和方法的信息。 >javap 学生 结论:如果我们仔细检查字节码,我们可能会有一些观察结果:
  • 编译器将Record关键字替换为class
  • 编译器将类声明为final。这表明该类无法扩展。这也意味着它不能被继承并且本质上是不可变的。
  • 转换后的类扩展了java.lang.Record。这表明所有记录都是java.lang包中定义的Record类的子类。
  • 编译器添加了参数化构造函数。
  • 编译器自动生成了toString()hashCode()equals()方法。
  • 编译器添加了访问字段的方法。注意方法命名约定 - 它们与字段名称完全相同,字段名称之前不应该有getset

记录中的字段

Java 中的记录使用一组字段定义其状态,每个字段都有不同的名称和类型。记录的字段在记录头中声明。例如:
public record Person(String name, int age) {}
该条目有两个字段:String类型的nameint类型的age。记录的字段是隐式最终的,并且在创建记录后不能重新分配。我们可以添加一个新字段,但不建议这样做。添加到记录的新字段必须是静态的。例如:
public record Person(String name, int age) {

   static String sex;
}

记录中的构造函数

与传统的类构造函数一样,记录构造函数用于创建记录实例。Records有两个构造函数的概念:规范构造函数和紧凑构造函数紧凑构造函数提供了一种更简洁的方式来初始化记录中的状态变量,而规范构造函数提供了一种更传统、更灵活的方式。

规范构造函数

默认的Java编译器为我们提供了一个全参数构造函数(全字段构造函数),它将其参数分配给相应的字段。它被称为规范构造函数。我们还可以添加条件语句等业务逻辑来验证数据。下面是一个例子:
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

紧凑型设计师

紧凑构造函数忽略所有参数,包括括号。相应的字段会自动分配。例如,以下代码演示了Records中紧凑构造函数的概念:
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
除了紧凑构造函数之外,您还可以在Record中定义常规构造函数,就像在常规类中一样。但是,您需要确保所有构造函数都初始化记录的所有字段。

记录中的方法

Java 中的记录会自动为记录中的每个字段生成一组方法。这些方法称为访问器方法,并且与它们关联的字段具有相同的名称。例如,如果记录有一个名为“价格”的字段,那么它将自动具有一个名为“价格”()的方法,该方法返回“价格”字段的值。除了自动生成的访问器方法之外,我们还可以在条目中定义自己的方法,就像在常规类中一样。例如:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
该条目有一个sayHello()方法,该方法使用name字段打印出问候语。

Record 如何帮助减少样板代码

让我们看一个简单的示例,了解 Java 表示法如何帮助消除样板代码。假设我们有一个名为Person的类,它代表一个具有姓名、年龄和电子邮件地址的人。这就是我们以传统方式定义类的方式。 不使用Record 的代码:
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
正如我们所看到的,这个类需要大量的样板代码来定义字段、构造函数、getter、setter 和toString()方法。另外,假设我们想让这个类不可变。为此,我们必须执行一些额外的步骤,例如:
  • 声明该类为最终类,以便它不能被扩展。
  • 将所有字段声明为私有最终,以便它们不能在构造函数之外更改。
  • 不要为字段提供任何setter方法。
  • 如果任何字段是可变的,您应该返回它们的副本而不是返回原始对象。
使用记录的代码:
public record Person(String name, int age, String email) {}
就这样!仅用一行代码,我们就定义了一个类,它具有与传统类相同的字段、构造函数、getter、setter 和toString()方法。Record语法为我们处理所有样板代码。从上面的示例可以清楚地看出,在 Java 中使用记录可以提供更简洁的语法来定义具有固定字段集的类,从而有助于消除样板代码。与传统方法相比,定义和使用记录所需的代码更少,并且记录使用更新字段的方法提供对字段的直接访问。这使得代码更具可读性、可维护性并且不易出错。此外,使用Record语法,我们不需要做任何额外的事情来使类不可变。默认情况下,记录中的所有字段都是最终的,并且记录类本身是不可变的。因此,使用 write 语法创建不可变类比传统方法更简单,并且需要更少的代码。对于记录,我们不需要将字段声明为final,提供初始化字段的构造函数,或者为所有字段提供getter。

公共记录类

Java 还可以定义通用的Records类。泛型入口类是具有一个或多个类型参数的入口类。这是一个例子:
public record Pair<T, U>(T first, U second) {}
在此示例中, Pair是一个通用记录类,它采用两个类型为TU的参数。我们可以按如下方式创建该记录的实例:
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Record 中的嵌套类

您还可以在条目中定义嵌套类和接口。这对于对相关的类和接口进行分组非常有用,并且可以帮助改进代码库的组织和可维护性。以下是包含嵌套类的条目的示例:
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
在此示例中,Person是包含嵌套Contact类的条目。反过来,Contact是一个静态嵌套类,包含两个私有最终字段:电子邮件地址和电话。它还具有一个接受电子邮件和电话号码的构造函数,以及两个 getter 方法:getEmail()getPhone()。我们可以像这样 创建一个Person实例:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
在此示例中,我们创建了一个名为John 、年龄 30 的新Person对象,以及一个电子邮件地址为 john@example.com 、电话为123-456-7890的新Contact对象。

Record 内的嵌套接口

以下是包含嵌套接口的示例条目:
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
在此示例中,Book是包含嵌套Edition接口的条目。反过来,Edition是一个定义单个getName()方法的接口。我们可以创建一个Book实例,如下所示:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
在此示例中,我们创建一个新的Book对象,其标题为Douglas Adams的《银河系漫游指南》,并创建一个新的Edition接口匿名实现,该实现在调用getName()方法时返回名称Science Fiction

记录还能做什么?

  • 条目可以定义自定义构造函数。记录支持参数化构造函数,它可以使用其主体中提供的参数调用默认构造函数。此外,记录还支持紧凑构造函数,它与默认构造函数类似,但可以包含附加功能,例如构造函数主体中的检查。
  • 与 Java 中的任何其他类一样,Record 可以定义和使用实例方法。这意味着我们可以创建和调用特定于录制类的方法。
  • Record中,不允许将实例变量定义为类成员,因为它们只能指定为构造函数参数。但是,记录支持静态字段和静态方法,它们可用于存储和访问记录类的所有实例所共有的数据。

记录可以实现接口吗?

是的,用Java编写可以实现接口。例如,下面的代码演示了这个概念:
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
这里我们定义了一个带有单个print()方法的Printable接口。我们还定义了一个实现Printable接口的Person条目。Person记录有两个字段:nameage,并重写Printable接口的 print 方法来打印这些字段的值。我们可以实例化一个Person条目并调用它的 print 方法,如下所示:
Person person = new Person("John", 30);
person.print();
这将在控制台输出Name: John, Age: 30。如示例所示,Java 记录可以像常规类一样实现接口。这对于向条目添加行为或确保条目符合接口定义的契约非常有用。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION