動態

詳情 返回 返回

一文講清楚什麼是serialVersionUID常數和瞬時變量 - 動態 詳情

大家好,我是 V 哥,今天來聊一聊serialVersionUID常數、瞬時變量,這幾個都是 Java 開發中比較基礎的概念,但容易被大家所忽視,V 哥通過一篇文章來介紹清楚,讓你無後顧之憂。先贊後看,家財萬貫。

以下是關於serialVersionUID常數和瞬時變量的詳細介紹:

serialVersionUID常數

  • 定義與作用serialVersionUID是Java中用於序列化和反序列化的一個重要概念。它是一個類的版本標識,用於確保在反序列化時,類的結構與序列化時的結構一致。如果類的serialVersionUID在序列化和反序列化之間發生了變化,JVM會認為這是兩個不同的類,從而導致反序列化失敗。
  • 原理:當一個類實現了java.io.Serializable接口,就可以被序列化和反序列化。在序列化過程中,JVM會根據類的結構和成員變量等信息生成一個serialVersionUID。在反序列化時,JVM會檢查這個serialVersionUID是否與序列化時的一致。
  • 案例

    import java.io.Serializable;
    
    class Person implements Serializable {
      // 顯式定義serialVersionUID
      private static final long serialVersionUID = 1L;
      private String name;
      private int age;
    
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    }
    
    public class Main {
      public static void main(String[] args) {
          // 創建一個Person對象並進行序列化和反序列化操作
          Person person = new Person("Alice", 30);
          // 這裏省略具體的序列化和反序列化代碼
      }
    }

    在上述代碼中,Person類實現了Serializable接口,並顯式定義了serialVersionUID為1L。如果沒有顯式定義,Java會根據類的結構等自動生成一個serialVersionUID,但這樣可能會導致在類的結構發生微小變化時,serialVersionUID也發生變化,從而導致反序列化問題。

瞬時變量

  • 定義與作用:在Java中,使用transient關鍵字修飾的變量被稱為瞬時變量。瞬時變量在對象序列化時不會被保存到序列化後的字節流中,在反序列化時會被賦予默認值。
  • 原理:這是因為transient關鍵字告訴了Java的序列化機制,在進行序列化操作時,忽略這個變量,不將其狀態保存到序列化數據中。
  • 案例

    import java.io.Serializable;
    
    class User implements Serializable {
      private String username;
      // 用transient修飾password,使其成為瞬時變量
      private transient String password;
    
      public User(String username, String password) {
          this.username = username;
          this.password = password;
      }
    }
    
    public class Main {
      public static void main(String[] args) {
          User user = new User("admin", "secret");
          // 這裏省略具體的序列化和反序列化代碼
          // 反序列化後,password將為null
      }
    }

    在上述代碼中,User類中的password變量被聲明為transient。當對User對象進行序列化和反序列化時,password變量的值不會被保存和恢復,反序列化後password的值將為null。這樣可以避免敏感信息(如密碼)在序列化過程中被泄露。

瞬時變量和瞬態變量有什麼區別?

瞬時變量和瞬態變量在Java等編程語言中實際上是同一個概念,英文都是“transient variable”,它們沒有本質區別,只是中文表述不同而已。以下從定義、使用場景、原理層面詳細説明:

  • 定義角度:兩者都是指被transient關鍵字修飾的變量。在對象序列化過程中,被transient修飾的變量不會被保存到序列化後的字節流中,在反序列化時會被賦予默認值。比如在Java中:

    import java.io.Serializable;
    
    class Data implements Serializable {
      private String normalVariable;
      // 這裏無論是叫瞬時變量還是瞬態變量,都是指這個被transient修飾的變量
      private transient String transientVariable;
    
      public Data(String normal, String transientValue) {
          normalVariable = normal;
          transientVariable = transientValue;
      }
    }
  • 使用場景角度

    • 保護敏感信息:對於像密碼這種敏感信息,將其定義為瞬態(瞬時)變量,就可以防止在對象序列化時密碼被泄露。因為在反序列化後,密碼字段會變為默認值,原密碼不會被恢復。
    • 避免不必要的數據存儲:有些變量的值在反序列化後可以通過其他方式重新計算或獲取,將這類變量設置為瞬態(瞬時)變量,可以減少序列化數據的大小,提高序列化和反序列化的效率。
  • 原理角度:在Java的序列化機制中,當遇到transient修飾的變量時,序列化框架會跳過該變量的寫入操作,在反序列化時也不會從字節流中讀取數據來恢復該變量的值,而是直接賦予其默認值,這就是瞬態(瞬時)變量在底層的實現原理。

除了保護敏感信息,還有哪些場景適合使用瞬態變量?

除了保護敏感信息外,以下是一些適合使用瞬態變量的常見場景:

  • 緩存數據

    • 場景描述:在程序運行過程中,經常會在內存中緩存一些數據以提高訪問性能。這些緩存數據通常是根據其他數據臨時計算或加載而來,在序列化對象時,不需要保存這些緩存數據,因為在反序列化後可以重新計算或加載。
    • 代碼示例
    import java.io.Serializable;
    
    class CacheData implements Serializable {
      private String originalData;
      // 用於緩存處理後的數據,不需要進行序列化
      private transient String processedCache;
    
      public CacheData(String data) {
          originalData = data;
      }
    
      public String getProcessedData() {
          if (processedCache == null) {
              // 模擬數據處理過程
              processedCache = originalData + " processed";
          }
          return processedCache;
      }
    }
  • 臨時狀態變量

    • 場景描述:當對象在運行期間會有一些臨時的狀態變量,這些變量僅在特定的方法調用或流程中起作用,並不屬於對象的核心持久化狀態。在序列化時,這些臨時狀態變量不需要被保存,以免在反序列化後造成狀態混亂。
    • 代碼示例
    import java.io.Serializable;
    
    class Transaction implements Serializable {
      private String transactionId;
      private String customerId;
      // 用於記錄交易是否正在進行的臨時狀態,不需要持久化
      private transient boolean inProgress;
    
      public Transaction(String id, String customer) {
          transactionId = id;
          customerId = customer;
      }
    
      public void startTransaction() {
          inProgress = true;
          // 執行交易開始的邏輯
      }
    
      public void endTransaction() {
          inProgress = false;
          // 執行交易結束的邏輯
      }
    }
  • 資源引用

    • 場景描述:如果對象中包含對一些外部資源(如文件流、網絡連接等)的引用,這些資源在序列化時無法被正確保存,而且在反序列化後也需要重新獲取。將這些資源引用聲明為瞬態變量,可以避免在序列化和反序列化過程中出現問題。
    • 代碼示例
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.Serializable;
    
    class FileProcessor implements Serializable {
      private String filePath;
      // 文件輸入流,不需要序列化,反序列化後重新打開
      private transient FileInputStream fileStream;
    
      public FileProcessor(String path) {
          filePath = path;
      }
    
      public void openFile() {
          try {
              fileStream = new FileInputStream(new File(filePath));
              // 執行文件讀取邏輯
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    
      public void closeFile() {
          try {
              if (fileStream!= null) {
                  fileStream.close();
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }
  • 事件監聽器或回調函數

    • 場景描述:在某些框架或應用中,對象可能會註冊一些事件監聽器或回調函數。這些監聽器或回調函數通常是與當前運行時的上下文相關,不適合被序列化。將它們聲明為瞬態變量,可以確保在序列化和反序列化過程中不會出現問題,並且在反序列化後可以重新註冊監聽器或設置回調函數。
    • 代碼示例
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    class EventEmitter implements Serializable {
      // 事件名稱
      private String eventName;
      // 存儲事件監聽器的列表,不需要序列化
      private transient List<Runnable> eventListeners = new ArrayList<>();
    
      public EventEmitter(String name) {
          eventName = name;
      }
    
      public void addEventListener(Runnable listener) {
          eventListeners.add(listener);
      }
    
      public void emitEvent() {
          for (Runnable listener : eventListeners) {
              listener.run();
          }
      }
    }

最後

好了,這下徹底知道什麼是serialVersionUID常數和瞬時變量了,使用起來會更加得心應手,關注威哥愛編程,全棧之路就你行。都看到這裏了,動動用小手點個小贊鼓勵一下V 哥唄,感謝老鐵。

user avatar dingtongya 頭像 lslove 頭像 zhangfeidezhu 頭像 lpicker 頭像 fsjohnhuang 頭像 r0ad 頭像 weirdo_5f6c401c6cc86 頭像 pengloo53 頭像 gaiya_68f1e7ba5b140 頭像 an_5cd4f637c2671 頭像
點贊 10 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.