簡體   English   中英

為什么 Java 有瞬態字段?

[英]Why does Java have transient fields?

為什么 Java 有瞬態字段?

Java 中的transient關鍵字用於指示字段不應成為序列化(這意味着已保存,例如保存到文件)過程的一部分。

來自Java 語言規范,Java SE 7 版第 8.3.1.3 節。 transient字段

變量可以被標記為transient以表明它們不是對象持久狀態的一部分。

例如,您可能有從其他字段派生的字段,並且只能以編程方式完成,而不是通過序列化來持久化狀態。

這是一個GalleryImage類,它包含一個圖像和一個從圖像派生的縮略圖:

class GalleryImage implements Serializable
{
    private Image image;
    private transient Image thumbnailImage;

    private void generateThumbnail()
    {
        // Generate thumbnail.
    }

    private void readObject(ObjectInputStream inputStream)
            throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();
        generateThumbnail();
    }    
}

在本例中, thumbnailImage是調用generateThumbnail方法生成的縮略圖。

thumbnailImage字段被標記為transient ,因此只有原始image被序列化,而不是同時保留原始圖像和縮略圖圖像。 這意味着保存序列化對象所需的存儲空間更少。 (當然,這可能是也可能不是理想的,這取決於系統的要求——這只是一個例子。)

在反序列化時,會調用readObject方法來執行將對象的狀態恢復到序列化發生時的狀態所需的任何操作。 這里需要生成縮略圖,所以重寫readObject方法,調用generateThumbnail方法生成縮略圖。

有關其他信息,文章Discover the Secrets of the Java Serialization API (最初可在 Sun Developer Network 上獲得)有一節討論了使用transient關鍵字來防止某些字段的序列化的情況,並提供了一個場景。

在理解transient關鍵字之前,必須先了解序列化的概念。 如果讀者知道序列化,請跳過第一點。

什么是序列化?

序列化是使對象的狀態持久化的過程。 這意味着對象的狀態被轉換成字節流以用於持久化(例如在文件中存儲字節)或傳輸(例如通過網絡發送字節)。 同樣,我們可以使用反序列化從字節中恢復對象的狀態。 這是 Java 編程中的重要概念之一,因為序列化主要用於網絡編程。 需要通過網絡傳輸的對象必須轉換為字節。 為此,每個類或接口都必須實現Serializable接口。 它是一個沒有任何方法的標記接口。

現在什么是transient關鍵字及其目的?

默認情況下,對象的所有變量都會轉換為持久狀態。 在某些情況下,您可能希望避免保留某些變量,因為您不需要保留這些變量。 因此,您可以將這些變量聲明為transient 如果變量被聲明為transient ,那么它將不會被持久化。 這就是transient關鍵字的主要目的。

我想用下面的例子來解釋以上兩點(借用這篇文章):

 package javabeat.samples; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; class NameStore implements Serializable{ private String firstName; private transient String middleName; private String lastName; public NameStore (String fName, String mName, String lName){ this.firstName = fName; this.middleName = mName; this.lastName = lName; } public String toString(){ StringBuffer sb = new StringBuffer(40); sb.append("First Name : "); sb.append(this.firstName); sb.append("Middle Name : "); sb.append(this.middleName); sb.append("Last Name : "); sb.append(this.lastName); return sb.toString(); } } public class TransientExample{ public static void main(String args[]) throws Exception { NameStore nameStore = new NameStore("Steve", "Middle","Jobs"); ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("nameStore")); // writing to object o.writeObject(nameStore); o.close(); // reading from object ObjectInputStream in = new ObjectInputStream(new FileInputStream("nameStore")); NameStore nameStore1 = (NameStore)in.readObject(); System.out.println(nameStore1); } }

輸出如下:

 First Name : Steve Middle Name : null Last Name : Jobs

中間名聲明為transient ,因此不會存儲在持久存儲中。

允許您定義不想序列化的變量。

在一個對象中,您可能有不想序列化/持久化的信息(可能是對父工廠對象的引用),或者序列化可能沒有意義。 將這些標記為“transient”意味着序列化機制將忽略這些字段。

為什么 Java 中需要瞬態字段?

transient關鍵字使您可以對序列化過程進行一些控制,並允許您從該過程中排除某些對象屬性。 序列化過程用於持久化 Java 對象,主要是為了在它們傳輸或不活動時保留它們的狀態。 有時,不序列化對象的某些屬性是有意義的。

您應該將哪些字段標記為瞬態?

現在我們知道了transient關鍵字和transient 字段的用途,重要的是要知道哪些字段要標記為transient。 靜態字段也不會序列化,因此相應的關鍵字也可以解決問題。 但這可能會破壞您的類設計; 這就是transient關鍵字派上用場的地方。 我盡量不允許其值可以從其他人派生的字段被序列化,因此我將它們標記為瞬態。 如果您有一個名為interest的字段,其值可以從其他字段( principal , rate & time )計算出來,則無需對其進行序列化。

另一個很好的例子是文章字數統計。 如果您要保存整篇文章,則實際上沒有必要保存字數,因為可以在文章“反序列化”時進行計算。 或者想想記錄器; Logger實例幾乎不需要序列化,因此它們可以是瞬態的。

transient變量是在類序列化時不包含的變量。

想到這可能有用的一個例子是,變量只在特定對象實例的上下文中有意義,並且一旦您序列化和反序列化對象就會變得無效。 在這種情況下,讓這些變量變為null很有用,這樣您就可以在需要時使用有用的數據重新初始化它們。

transient用於指示類字段不需要序列化。 最好的例子可能是Thread字段。 通常沒有理由序列化Thread ,因為它的狀態非常“特定於流”。

除了本機 java 之外的序列化系統也可以使用此修飾符。 例如,Hibernate 不會保留標有@Transient瞬態修飾符的字段。 兵馬俑也尊重這個修飾符。

我相信修飾符的比喻意義是“該字段僅用於內存中。不要以任何方式將其持久化或移動到此特定 VM 之外。它不可移植”。 即你不能依賴它在另一個 VM 內存空間中的值。 很像volatile意味着你不能依賴某些內存和線程語義。

在我回答這個問題之前,我需要解釋一下序列化,因為如果你理解了科學計算機中序列化的含義,你就很容易理解這個關鍵字了。

當對象通過網絡傳輸/保存在物理媒體(文件,...)上時,對象必須被“序列化”。 序列化轉換字節狀態對象系列。 這些字節在網絡上發送/保存,並從這些字節重新創建對象。

例子:

public class Foo implements Serializable 
{
 private String attr1;
 private String attr2;
 ...
}

現在,如果這個類中有一個字段你不想轉移或保存,你可以使用transient關鍵字

private transient attr2;

這可以防止在類序列化時包含字段表單。

因為並非所有變量都具有可序列化的性質

當您不想共享一些與序列化相關的敏感數據時需要它。

根據谷歌瞬態含義 == 僅持續很短的時間; 暫時的。

現在,如果您想在 java 中使任何東西變得瞬態,請使用瞬態關鍵字。

問:在哪里使用瞬態?

A:一般在java中我們可以通過在變量中獲取數據並將這些變量寫入文件來將數據保存到文件中,這個過程被稱為序列化。 現在,如果我們想避免將變量數據寫入文件,我們可以將該變量設為瞬態。

transient int result=10;

注意:瞬態變量不能是局部的。

瞬態關鍵字的簡化示例代碼。

import java.io.*;

class NameStore implements Serializable {
    private String firstName, lastName;
    private transient String fullName;

    public NameStore (String fName, String lName){
        this.firstName = fName;
        this.lastName = lName;
        buildFullName();
    }

    private void buildFullName() {
        // assume building fullName is compuational/memory intensive!
        this.fullName = this.firstName + " " + this.lastName;
    }

    public String toString(){
        return "First Name : " + this.firstName
            + "\nLast Name : " + this.lastName
            + "\nFull Name : " + this.fullName;
    }

    private void readObject(ObjectInputStream inputStream)
            throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();
        buildFullName();
    }
}

public class TransientExample{
    public static void main(String args[]) throws Exception {
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ns"));
        o.writeObject(new NameStore("Steve", "Jobs"));
        o.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ns"));
        NameStore ns = (NameStore)in.readObject();
        System.out.println(ns);
    }
}

簡單地說,transient java 關鍵字保護字段不被序列化作為它們的非瞬時字段計數器部分。

在這個代碼片段中,我們的抽象類 BaseJob 實現了 Serializable 接口,我們從 BaseJob 擴展,但我們不需要序列化遠程和本地數據源; 僅序列化 organizationName 和 isSynced 字段。

public abstract class BaseJob implements Serializable{
   public void ShouldRetryRun(){}
}

public class SyncOrganizationJob extends BaseJob {

   public String organizationName;
   public Boolean isSynced

   @Inject transient RemoteDataSource remoteDataSource;
   @Inject transient LocalDaoSource localDataSource;

   public SyncOrganizationJob(String organizationName) {
     super(new 
         Params(BACKGROUND).groupBy(GROUP).requireNetwork().persist());

      this.organizationName = organizationName;
      this.isSynced=isSynced;

   }
}

使用瞬態修飾符聲明的字段不會參與序列化過程。 當一個對象被序列化(保存在任何狀態)時,其瞬態字段的值在序列表示中被忽略,而瞬態字段以外的字段將參與序列化過程。 這就是transient關鍵字的主要目的。

因為並非所有變量都具有可序列化的性質。

  1. 序列化和反序列化是對稱過程,如果不是你不能期望結果是確定的,在大多數情況下,未確定的值是沒有意義的;
  2. 序列化和反序列化是冪等的,這意味着您可以根據需要進行多次序列化,並且結果是相同的。

所以如果Object可以存在於內存中而不能存在於磁盤上,那么這個Object就不能被序列化,因為反序列化時機器無法恢復內存映射。 例如,您不能序列化Stream對象。

您不能序列化Connection對象,因為它的狀態也依賴於遠程站點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM