Java序列化:Serializable 和 Externalizable 接口

記一次面試:

面試官:怎麼實現實體的序列化?

答:實現了Serializable接口

面試官:哦,確定嗎?還有其他答案嗎?

答:確定,暫時沒有

面試官:那怎麼避免某個字段參與序列化?

答:使用transient關鍵字。

面試官:哦,確定嗎?還有其他答案嗎?

答:確定啊

面試掛,給我反饋是基礎知識不紮實。。。。

 

測試類:

Player 和 Weapon 都實現了Serializable接口(成員變量引用的類也必須實現了Serializable接口)

public class Player implements Serializable {

    private static final long serialVersionUID = -75L;

    private String name;
    private int level;
    private Weapon weapon;

    public Player(){
        name = "sam";
        level = 10;
        weapon = new Weapon();
    }
    
    @Override
    public String toString() {
        return "Player{" +
                "name='" + name + '\'' +
                ", level=" + level +
                ", weapon=" + weapon.getName() +
                '}';
    }
}
public class Weapon implements Serializable {

    private static final long serialVersionUID = -89812L;

    private String name;

    public Weapon(String name) {
        this.name = name;
    }

    public Weapon() {
        this.name = "AK74";
    }

    public String getName() {
        return name;
    }

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

}

使用ObjectOutputStream 和 ObjectInputStream 實現序列化和反序列化

        File of = new File("/Users/sam/ooof");

        /**
         *  Serializable 開始
         */

        Player r1 = new Player();

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(of));

        oos.writeObject(r1);

        oos.close();
        

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(of));

        Player afterRead = (Player) ois.readObject();

        ois.close();

        System.out.println(afterRead);

        System.out.println("------------------------------------------------------");

可以看到r1被寫入文件,再次讀取出來之後,成員變量的值仍保留。

使用transient關鍵字來阻止字段被序列化:

再次執行測試例程,可見name字段是空的:

 

實現Externalizable接口(非自動序列化):

PlayerEx類,實現Externalizable接口中的writeExternal和readExternal方法,其中Weapon類不是必須實現Externalizable接口了,只要實現Serializable接口即可:

package com.sam.uvb76.book.serializable;

import java.io.*;

public class PlayerEx implements Externalizable {

    private static final long serialVersionUID = -75L;

    private transient String name;
    private int level;
    private Weapon weapon;

    public PlayerEx(){
        name = "sam";
        level = 10;
        weapon = new Weapon();
    }

    @Override
    public String toString() {
        return "Player{" +
                "name='" + name + '\'' +
                ", level=" + level +
                ", weapon=" + weapon.getName() +
                '}';
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(level);
        out.writeObject(weapon);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        level = (int) in.readObject();
        weapon = (Weapon) in.readObject();
    }
}

再次執行例程:

可見,name字段雖然有 transient關鍵字約束,但仍會被序列化和逆序列化。

那怎麼讓name字段不參與序列化:writeExternal中將對應字段寫入空值,或readExternal讀出後自己設置爲空值即可。

經過試驗,如果writeExternal和readExternal不對某個字段處理,該字段還是會成功參與序列化的。

transient關鍵字只能和Serializable接口搭配使用,Externalizable接口默認所有字段參與序列化

 

注意:實現Externalizable接口的實體類需有public無參構造方法

即:要麼自己寫一個形參爲空構造方法,要麼不寫,編譯器會自動生成一個空的構造方法。但實體類中不可只寫一個有參構造方法,這樣編譯器不會幫我們生成那個無參的構造方法了。例如:

執行測試例程執行會報異常:

 

實現Externalizable接口,優勢在於通過writeExternal和readExternal方法,可以在序列化或逆序列化過程中對成員字段加入自己的處理邏輯,更加靈活:

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(level + 100);
        out.writeObject(weapon);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name =  in.readObject() + "Externalizable接口實現的";
        level = (int) in.readObject();
        weapon = (Weapon) in.readObject();
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章