JavaRush /Java Blog /Random-TW /喝咖啡休息#230。Java 中的記錄是什麼以及它們如何運作?

喝咖啡休息#230。Java 中的記錄是什麼以及它們如何運作?

在 Random-TW 群組發布
資料來源: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