序列化探究一二
Serializable原理
seriaVersionUid的作用
自定義序列化及反序列化過程
磁盤保存文件,進程間數據交互,網絡傳輸數據等都是先將數據轉換爲字節序列,收到字節序列後再轉換爲圖片,視頻或其他文件。
這一過程就需要序列化和反序列化的支持。Java中序列化是通過==ObjectOutputStream.writeObject()== 方法將Object轉爲字節序列後寫到輸出流中。反序列化是通過==ObjectInputStream.readObject()== 將字節序列轉爲一個對象。
先上代碼:
1.定義一個User類,這個類先不繼承Serializable
import java.io.Serializable;
public class User implements Serializable{
private int id;
private String name;
private int age;
private boolean sex;
public User(int id,String name,int age,boolean sex){
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
}
2.實現序列化
public static void enSerializable() throws IOException{
FileOutputStream fo = new FileOutputStream(new File("D:/user.txt"));
ObjectOutputStream oo = new ObjectOutputStream(fo);
oo.writeObject(new User(1,"wangliang",22,true));
oo.close();
}
3.反序列化實現
public static void deSerializable() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("D:/user.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
User user = (User)oi.readObject();
System.out.println("User對象反序列化成功!"+user.getName()+user.getAge());
}
運行報錯: java.io.NotSerializableException
但是打開user.txt 發現裏面已經寫入了數據,雖然都是亂碼。此時反序列化失敗。
修改User類讓其繼承Serializable,正常序列化和反序列化。
serialVersionUID的作用
主要是爲了類的兼容性。
想象一下這種場景:
昨天將學生類的數據保存到了磁盤上,今天取數據之前要在學生類上多加一個字段,如果沒有指定serialVersionUID,會報錯,因爲編譯器認爲這兩個類不一致了。此時如果加上serialVersionUID可以正常反序列化。
通常有兩種賦值方式,一種是1L,2L這種,另外一種是64位的Hash值,用那種看你,如果想保持向後兼容,就讓serialVersionUID一致,否則改成不一樣就好了。
自定義序列化及反序列化過程
方法一:若Student類僅僅實現了Serializable接口,則可以按照以下方式進行序列化和反序列化
ObjectOutputStream採用默認的序列化方式,對Student對象的非transient的實例變量進行序列化。
ObjcetInputStream採用默認的反序列化方式,對對Student對象的非transient的實例變量進行反序列化。
方法二:若Student類僅僅實現了Serializable接口,並且還定義了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),則採用以下方式進行序列化與反序列化。
ObjectOutputStream調用Student對象的writeObject(ObjectOutputStream out)的方法進行序列化。
ObjectInputStream會調用Student對象的readObject(ObjectInputStream in)的方法進行反序列化。
方法三:若Student類實現了Externalnalizable接口,且Student類必須實現readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,則按照以下方式進行序列化與反序列化。
ObjectOutputStream調用Student對象的writeExternal(ObjectOutput out))的方法進行序列化。
ObjectInputStream會調用Student對象的readExternal(ObjectInput in)的方法進行反序列化。
說明:
1.對於方法一:如果某個字段不想進行序列化和發序列化,那麼對該字段加transient關鍵字修飾即可。
2.方法二和方法三都可以控制序列化和反序列化過程。不同的是方法二在readObject()和writeObject()中進行控制,而且這兩個方法不一定必須實現;方法三readExternal()和writeExternal()必須實現,同時繼承了 Externalizable接口後不會再執行readObject()和writeObject()方法。
3.自定義序列化過程在實際中用到的可能是控制反序列化後對象的字段順序,以前項目中遇到過用字段來驗籤,結果每次序列化後字段順序不一樣導致驗籤不過,這個問題可以通過自定義序列化過程來解決。
* 繼承Externalizable接口代碼:*
@Override
public void readExternal(ObjectInput arg0) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
setAge(arg0.readInt());
setName((String)arg0.readObject());
setId(arg0.readInt());
setSex(arg0.readBoolean());
}
@Override
public void writeExternal(ObjectOutput arg0) throws IOException {
// TODO Auto-generated method stub
arg0.writeInt(getId());
arg0.writeObject(getName());
arg0.writeInt(getAge());
arg0.writeBoolean(isSex());
System.out.println("User對象序列化成功!");
}