Serializable 接口(序列化)
前言
查看API文檔時,就會發現
Serializable
接口是一個標記接口(沒有成員方法和變量),那么他有什么用呢?
- 序列化:可以將一個對象(標志對象的類型)及其狀態轉換為字節碼,保存起來(可以保存在數據庫,內存,文件等),然后可以在適當的時候再將其狀態恢復(也就是反序列化)
- 一個類要想序列化就必須繼承java.io.Serializable接口,同時它的子類也可以序列化(不用再繼承
Serializable
接口)。Serializable
接口,不僅可以本機,也可以網絡操作,它自動屏蔽了操作系統的差異,字節順序等。- 序列化只能保存對象的非靜態成員變量,不能保存任何的成員方法和靜態的成員變量,而且序列化保存的只是變量的值,對於變量的任何修飾符都不能保存。記住序列化是保存對象的狀態。
用途
- 想把的內存中的對象狀態保存到一個文件中或者數據庫中時候。
- 想把對象通過網絡進行傳播的時候。
如何實現
- 原理:Java 序列化技術可以使你將一個對象的狀態寫入一個Byte 流里(系列化),並且可以從其它地方把該Byte 流里的數據讀出來(反序列化)。
- 通過
ObjectOutputStream
的writeObject()
方法把這個類的對象寫到一個地方(文件),再通過ObjectInputStream
的readObject()
方法把這個對象讀出來。
- 先寫一個實現序列化的類Person
class Person implements Serializable{
//序列化的關鍵常量
private static final long serialVersionUID = -3911255650485738676L;
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
- 用
ObjectOutputStream
的writeObject()
方法把這個類的對象寫到一個地方,再通過ObjectInputStream
的readObject()
方法把這個對象讀出來。
public static void main(String[] args) throws Exception {
write();
read();
}
public static void read() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
}
public static void write() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
Object obj = new Person("老王", 40);
oos.writeObject(obj);
oos.close();
}
}
異常
-
java.io.NotSerializableException
- 名稱:沒有序列化異常
- 原因:在將對象保存到文件系統的時候沒有將對象實現序列化接口
- 解決:針對需要寫入到文件系統的對象實現對應的序列化接口
-
InvalidClassException
- 名稱:無效類異常
- 原因:文件中保存的流的序列化id和本地類文件的序列化id不匹配(
serialVersionUID
) - 解決:保證id一致性
serialVersionUID
serialVersionUID
的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID
的取值有可能也會發生變化。- 類的
serialVersionUID
的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID
,也有可能相同。一般給serialVersionUID
賦予明確的值。- 當通過網絡傳輸,因
serialVersionUID
不一致的時候就會報InvalidClassException
transient關鍵字
- 阻止實例中那些用此關鍵字聲明的變量持久化;當對象被反序列化時(從源文件讀取字節序列進行重構),這樣的實例變量值不會被持久化和恢復。
- 簡單的說:當某些變量不想被序列化,同是又不適合使用static關鍵字聲明,那么此時就需要用transient關鍵字來聲明該變量。(不會再反序列化的時候被獲取數據,只會取得初始值,如 int 型的是 0,對象型的是 null。)
class Person implements Serializable{
//序列化的關鍵常量
private static final long serialVersionUID = -3911255650485738676L;
private String name;
//反序列化時不會獲取age值
private transient int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
以上
@Fzxey