java自定義序列化

寫不完作業還要玩發表於2021-11-15

自定義序列化

1.問題引出

在某些情況下,我們可能不想對於一個物件的所有field進行序列化,例如我們銀行資訊中的設計賬戶資訊的field,我們不需要進行序列化,或者有些field本省就沒有實現Serializable介面。

java中的序列化是遞迴序列化,也就是你的field的引用型別中也有field可以被序列化,那麼就會在序列化當前物件的時候,一同序列化

2.解決辦法

使用transient(瞬變現象;過往旅客;候鳥)關鍵字來修飾,該關鍵字只能修飾屬性,這樣在序列化的時候,這個屬性就會用預設值,例如int型別用0,引用物件用null;

但是使用transient關鍵字修飾的field雖然簡單方便,但是會被完全隔離在序列化機制之外,這樣導致在反序列化回覆java物件的時候,無法取得該field的值。

因此我們可以使用自定義序列化機制,可以讓程式控制如何序列化各field,甚至完全不序列化某些field(這樣就與transient相同),在序列化和反序列化過程中需要特殊處理的類應該提供如下特殊簽名的方法,這些特殊的方法用以實現自定義的序列化

private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException;
private void readObejctNoData()throws ObejctStreamException;

熱愛你所寫的每一行的程式碼

writeObject()方法負責寫入特定類的例項狀態,通過重寫這個方法,程式設計師可以完全獲得對序列化機制的控制,可以自主決定那些field需要序列化,需要怎麼序列化,預設情況(函式體為空)該方法會呼叫out.defaultWriteObject來儲存java物件的各field,從而達到實現序列化java物件狀態的目的

readObject負責從流中讀取並且回覆物件的field,通過重寫該方法,程式設計師,可以獲得對反序列化機制的控制,對於反序列化各個field的順序應該和序列化各個field的順序相同。

至於當序列化流不完整時,readObjectNoData可以正確的初始化反序列化的物件,例如接收方接收到的序列化流殘缺,或者序列化版本不同,則使用readObjectNoData來預設的初始化。

例子(對於person的改寫):

class Person implements Serializable
{
	private String name;
	private int age;
	public Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	//自動生成的Get和Set方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	private void writeObject(ObjectOutputStream out) throws IOException
	{
		//將名字翻轉之後寫入二進位制流
		out.writeObject(new StringBuffer(this.name).reverse());
		out.writeInt(this.age);
	}
	private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException
	{
		this.name=((StringBuffer)in.readObject()).reverse().toString();
		//會丟擲異常,因為這裡的這樣寫法導致同
		this.age=in.readInt();
	}

}

應該提醒的是,這個自定義的功能十分強大

另外一種替換性的改寫:

//注意:這個方法由序列化機制呼叫,只要該方法存在就,它的訪問控制符就可以為private protected package-private中的任意一種
	private Object writeReplace() throws ObjectStreamException
	{
		ArrayList<Object> list=new ArrayList<>();
		list.add(name);
		list.add(age);
		//我們這裡返回ArrayList
		return list;
	}

序列化機制保證在序列化某個物件之前,先呼叫該物件的writeReplace方法,如果該方法返回另外一個java物件,系統就轉換為序列化writeReplace的返回結果。(ps:如果這個返回結果也有writeReplace方法的話,就繼續遞迴替代,直到沒有替換)

相應與writeReplace相對的有一個readResolve方法,這個方法保護性的賦值整個物件,這裡就不展開討論了。

3.另外一種自定義序列化機制(介紹Externalizable)

Java還提供了另一種序列化機制,這種序列化方式完全由程式設計師決定儲存和恢復物件資料。要實現該目標,Java類必須實現Externalizable介面,該介面裡定義瞭如下兩個方法。

  • void readExternal(ObjectInput in):需要序列化的類實現readExternal()方法來實現反序列化。該方法呼叫DataInput(它是ObjectInput的父介面)的方法來恢復基本型別的Field值,呼叫ObjectInput的readObject()方法來恢復引用型別的Field值。
  • void writeExternal(ObjectOutput out):需要序列化的類實現writeExternal()方法來儲存物件的狀態。該方法呼叫DataOutput(它是ObjectOutput的父介面)的方法來儲存基本型別的Field值,呼叫ObjectOutput的writeObject()方法來儲存引用型別的Field值。

具體的實現方式與上面自定義Serializable介面的實現類的序列化是相同的操作,這裡就不闡述了,下面圖是二者的比較。

相關文章