Java 中的 Serilizable 接口和 transient 關鍵字
Serilizable 接口
Serilizable 接口是一個辨別接口,沒有方法和變量,jdk 中的描述為:實作了該接口的類具有可序列化性。所謂可序列化性即對象可以被序列化成位元組碼,用于存儲到磁盤或者進行網絡傳輸,同時又可以從位元組碼反序列化成對象。
父類實作了 Serilizable 接口,其子類也是可序列化的。
如果類實作了 Serilizable 接口 ,但是其一個引用成員變量沒有實作 Serilizable 接口将抛出 NotSerializableException 異常。
下面是序列化和反序列化的一些 Demo:
public class TestWrite {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
Demo demo = new Demo(1,"1");
oos.writeObject(demo);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class TestRead {
public static void main(String[] args) {
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
Demo demo = (Demo)ois.readObject();
System.out.println(demo.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Demo implements Serializable {
private int var1;
private String var2;
public Demo(int var1, String var2) {
this.var1 = var1;
this.var2 = var2;
}
@Override
public String toString() {
return "Demo["+var2+","+var2+"]";
}
}
當我們再執行反序列化前,即執行上面代碼 TestRead 類的 main 方法前,調用修改了 Demo 類,會導緻反序列化抛出異常:
class Demo implements Serializable {
private int var1;
private String var2;
private String var3;
public Demo(int var1, String var2) {
System.out.println('o');
this.var1 = var1;
this.var2 = var2;
}
@Override
public String toString() {
return "Demo["+var2+","+var2+"]";
}
}
___________________________________________
java.io.InvalidClassException: dataStructure.test.Demo; local class incompatible: stream classdesc serialVersionUID = -8932904352177221623, local class serialVersionUID = 6808531953279345133
異常為 serialVersionUID 不一緻導緻,即序列化前的對象和和目前對象版本不一緻。關于 serialVersionUID 将在後面講到。
transient 關鍵字
當我們實作 serilizable 接口,但是又不想将類中部分變量序列化,就可以在變量前加上 transient 關鍵字,保證其不會被序列化,當然 transient 發揮的作用會被一些自定義序列化方法破壞。
class Demo implements Serializable {
private int var1;
private transient String var2;
public Demo(int var1, String var2) {
this.var1 = var1;
this.var2 = var2;
}
public String toString() {
return "Demo["+var1+","+var2+"]";
}
}
___________________________________________
Demo[1,null]
自定義序列化
上面說到自定義序列化方法即:
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectIutputStream in) throws IOException,ClassNotFoundException;
private Object writeReplace() throws ObjectStreamException
private Object readResolve() throws ObjectStreamException
private void readObjectNoData() throws ObjectStreamException;
通過readObject和writeOjbect方法可以幹預序列化,例如把待序列化的對象的某個屬性字段加密和解密,或者把transient關鍵字标記的屬性序列化寫出去。
class Demo implements Serializable {
private int var1;
private transient String var2;
public Demo(int var1, String var2) {
this.var1 = var1;
this.var2 = var2;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(var1 +10);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
var1 = in.readInt() + 10;
}
public String toString() {
return "Demo["+var1+","+var2+"]";
}
}
___________________________________________
Demo[21,null]
writeReplace方法會在writeObject方法之前執行。 ObjectOutputStream會把writeReplace方法傳回的對象序列化寫出去
readResolve方法會在readObject方法之後執行,可以再次修改readObject方法傳回的對象資料。
當序列化流不完整時,readObjectNoData()方法可以用來正确地初始化反序列化的對象。
serialVersionUID
serialVersionUID 是 Java 為實作 Serilizable 接口的類産生的版本辨別,確定反序列化時發生方和接收方是相容的。如果序列化前後 serialVersionUID 不一緻則會抛出 InvalidClassException 異常。建議顯示定義 serialVersionUID 的值, JDK 版本不一緻或者 IDE 重新編譯使自動生成的 serialVersionUID 不一緻,進而導緻反序列化錯誤。
上面因為新增一個變量導緻抛出異常可以通過顯示定義 serialVersionUID 的 方法實作:
class Demo implements Serializable {
private final static long serialVersionUID = 1L;
private int var1;
private String var2;
private String var3;
public Demo(int var1, String var2) {
this.var1 = var1;
this.var2 = var2;
}
public String toString() {
return "Demo["+var1+","+var2+"]";
}
}
____________________________________________
Demo[1,1]
Externalizable 接口
Externalizable 接口不同于 Serializable 接口,實作此接口必須實作接口中的兩個方法實作自定義序列化,這是強制性的;特别之處是必須提供pulic的無參構造器,因為在反序列化的時候需要反射建立對象。
class Demo implements Externalizable {
private final static long serialVersionUID = 1L;
private int var1;
private String var2;
public Demo(int var1, String var2) {
this.var1 = var1;
this.var2 = var2;
}
public Demo() {
}
public String toString() {
return "Demo["+var1+","+var2+"]";
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
StringBuffer reverse = new StringBuffer(var2).reverse();
out.writeInt(var1);
out.writeObject(reverse);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.var1 = in.readInt();
this.var2 = ((StringBuffer) in.readObject()).reverse().toString();
}
}