Java對象序列化

一、概念

序列化:把Java對象轉換爲字節序列的過程

反序列化:把字節序列恢復爲Java對象的過程

二、用途

(1)把對象的字節序列保存到硬盤

(2)在網絡上傳送對象的字節序列

三、對象序列化

只有實現了Serializable和Externalizable接口的類的對象才能被序列化。

1、實現Serializable接口

java.io.ObjectOutputStream代表對象輸出流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的字節序列寫到目標輸出流中。

java.io.ObjectInputStream代表對象輸入流,它的readObject()方法從一個源輸入流中讀取字節序列,再把他們反序列化爲一個對象,並將其返回。

示例:

package com.learns.Serializable;

import java.io.Serializable;

public class User implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	public User(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
}
序列化反序列化:

package com.learns.Serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Demo {
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		// 序列化
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\user.obj"));
		User user = new User("sky",12);
		out.writeObject(user);
		out.close();
		
		// 反序列化
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\user.obj"));
		User obj = (User) in.readObject();
		System.out.println(obj.toString());
		in.close();
	}
}

ObjectOutputStream只能對Serializable接口的類的對象進行序列化。

對象的默認序列化機制寫入的內容是:對象的類,類簽名,以及非瞬態(非transient的實例變量)和非靜態字段的值。

ObjectOutputStream按照默認方式反序列化時,具有如下特點:

1)如果在內存中對象所屬的類還沒有被加載,那麼會先加載並初始化這個類。如果在classpath中不存在相應的類文件,那麼會拋出ClassNotFoundException

2)在反序列化時不會調用類的任何構造方法。如果希望控制類的序列化方式,可以在可序列化類中提供writeObject()和readObject()方法。

使用場景:有些對象含敏感信息,如果按照默認方式序列化,在網絡上傳輸,可能會被不法份子竊取。對於這類信息,可以進行加密後再序列化。反序列化時則需要解密。

注:多個對象時,讀取對象的順序與寫入時的順序要一致。

2、實現Externalizable接口

Externalizable接口繼承自Serializable接口,如果一個類實現了Externalizable接口,那麼將完全由這個類控制自身的序列化行爲。Externalizable接口聲明瞭兩個方法:

	// 序列化對象時,該方法自動調用
	public void writeExternal(ObjectOutput out) throws IOException {}

	// 反序列化對象時,該方法自動調用
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}
在對實現了Externalizable接口的類的對象進行反序列化時,會先調用類的不帶參數的構造方法。這是有別於默認反序列方式的。如果把類的不帶參數的構造方法刪除,或者把該構造方法的訪問權限設置爲private、默認或protected級別,會拋出java.io.InvalidException: no valid constructor異常。
示例:

package com.learns.Serializable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class People implements Externalizable {
	
	private String name;
	private int age;
	
	public People() {}
	
	public People(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "People [name=" + name + ", age=" + age + "]";
	}
	

	@Override
	// 序列化對象時,該方法自動調用
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(name);
		out.writeObject(age);
	}

	@Override
	// 反序列化對象時,該方法自動調用
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		this.name = (String) in.readObject();
		this.age = (int) in.readObject();
	}

}
測試:
package com.learns.Serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Demo2 {
	
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		// 序列化
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\people.obj"));
		People people = new People("sky",12);
		out.writeObject(people);
		out.close();
		
		// 反序列化
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\people.obj"));
		People obj = (People) in.readObject();
		System.out.println(obj.toString());
		in.close();
	}
	
}
四、可序列化類的不同版本的序列化兼容性

實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量:

	private static final long serialVersionUID = 1L;
以上serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。
類的serialVersionUID的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID,也有可能相同。爲了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,爲它賦予明確的值。顯式地定義serialVersionUID有兩種用途:
1) 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
2) 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID









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