一、概念
序列化:把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