記一次面試:
面試官:怎麼實現實體的序列化?
答:實現了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(); }