「OpenJdk-11 源碼-系列」Serializable

Serializable 是什麼

Serializable 是一種用來處理對象流的機制。對象流就是將運行時對象的狀態進行流化,即轉換成二進制。說白了,就是將正在運行中程序的對象的狀態/內容轉換成二進制進行傳輸,持久化的操做。java

在 Java 中,序列化是在 jdk 1.1 時引入的特性。Serializable 的定義安全

public interface Serializable {
}
複製代碼

只有這麼個接口,一旦某類實現了該接口,那麼該類就有了序列化的能力,但嚴格來講,不必定能序列化/反序列化成功,爲撒吶?先來看個 demo數據結構

Quickly start

舉個栗子🌰,有一 Women類,該類有三個屬性,分別是age,weightname,咱們來試試怎麼序列/反序列化。ide

先來個 Women classui

//Women.class
public class Women implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Integer age;
    private String name;
    private Float weight;

    public Women(String name, int age, float weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
    
   '''
   setter/getter    
   '''
}
複製代碼

序列化

public static void main(String[] args) throws IOException {
        Women women = new Women("xiao ju",18, 45.1f);
        File file = new File("xiaoju.txt");
        FileOutputStream output = new FileOutputStream(file);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(output);
        objectOutputStream.writeObject(women);
        output.close();
        objectOutputStream.close();
    }
複製代碼

反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
        File file = new File("xiaoju.txt");
        FileInputStream input= new FileInputStream(file);
        ObjectInputStream objectInputStream = new ObjectInputStream(input);
        Women women = (Women) objectInputStream.readObject();
        input.close();
        objectInputStream.close();
    }
複製代碼

這就是序列化/反序列化的過程。能夠看到咱們在Woemen類中,不只實現了Serializable接口外,還加了一個serialVersionUID,固然了,若是咱們不手動加,那麼系統也會根據當前類的結構會初始化一個UID,它的做用是什麼呢?this

當進行序列化的時候,UID會和當前對象的狀態一同持續久化,在反序列化的時候,會檢測當前類的UID是否和持久化的UID一致,若是一致,那麼反序列化成功,不然失敗。spa

若是咱們沒有指定UID的化,系統在初始化一個UID後,隨着對象的持久化,若是咱們這個時候改了該類的數據結構,那麼這個類的UID就會發生改變(系統初始化的UID是根據類的數據結構算出來的),因此在反序列化的時候,就會檢測到UID不一致,那麼就會失敗。因此,通常咱們都會加上一個UIDcode

那問題來了,所有類都支持序列化很差嘛?

確實很差,不要你以爲,要我以爲,不是全部的類都想要序列化的,有些類是比較敏感的,好比用戶類,卡號類... 對於序列化的對象的信息是很容易被破解的,不能保證其安全性。cdn

問題又來了,我只想要序列化部分怎麼辦

兩種方式:對象

  1. 在不想要被序列化的屬性上添加static修飾符。前面咱們說到,序列化/反序列化的都是程序正在運行中的對象的狀態,而被static修飾的屬性/方法實際上是屬於類的狀態,不屬於處於內存中的對象。
  2. 在不想要被序列化的屬性上添加transient修飾,transient不能修飾方法/類。一旦屬性被transient修飾,那麼該屬性就不會被序列/反序列化。

序列化規則

  1. 類成員

    爲了減小存儲,傳輸空間,從提升效率,那麼在進行序列化的時候,能夠將不須要序列化的屬性經過 statictransient關鍵字修飾排除掉。

  2. 繼承關係

    • 若是父類實現了Serializable接口,子類序列化後,那麼父類也會實現系列化
    • 若是父類沒有實現Serializable接口,子類被序列化後,父類將不會被序列化
  3. 引用關係

    若是一個類實現了序列化,而且引用了一個對象。該對象所屬類實現了Serializable接口,那麼會改對象也會被序列化,不然會拋出java.io.NotSerializableExeception

Serializable 子接口 Externalizable

前面咱們說到,實現了Serializable接口的類,能夠將整個或部分對象的狀態進行序列化/反序列化。那麼若是咱們想要更加定製化的序列化/反序列化一些東西的話,那麼咱們就須要用到Externalizable,好比:在序列化的時候我想保存一個時間,反序列化的時候我但願獲取到這個時間,可是這個時間我不想放到類中。

public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

複製代碼

能夠看到Externalizable除了實現了Serializable接口外,還新增了兩個方法,這兩個方法就是能夠在序列化和反序列化的時候作一些定製的東西。仍是拿Women來看。

//Women.class
public class Women implements Externalizable {
    private static final long serialVersionUID = 1L;
    
    private Integer age;
    private String name;
    private Float weight;

    public Women(String name, int age, float weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(new Date());
        out.writeObject(this.name);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println(in.readObject());
        this.name = (String) in.readObject();
    }
    
   '''
   setter/getter    
   '''
}
複製代碼
相關文章
相關標籤/搜索