概念
- 序列化就是將對象轉化為字節(jié)流。
- 反序列化就是將字節(jié)流轉化為對象。
- 默認的序列化是深度系列化(即類中嵌套其他對象引用的對象也會被序列化)。
- 靜態(tài)成員不會被默認序列化,要讓一個類支持序列化只要讓這個類實現(xiàn)接口 java.io.Serializable 即可
package java.io;
public interface Serializable {
}
以上是 Serializable
的接口定義,且 Serializable
只是一個沒有定義任何方法的標記接口。
為什么定義標記接口即可實現(xiàn)序列化了呢?
聲明實現(xiàn) Serializable
接口后保存讀取對象就可以使用 ObjectOutputStream
、ObjectInputStream
流了,ObjectOutputStream
是 OutputStream
的子類,但實現(xiàn)了 ObjectOutput
接口,ObjectOutput
是 DataOutput
的子接口,增加了一個 writeObject(Object obj)
方法將對象轉化為字節(jié)寫到流中,ObjectInputStream
是 InputStream
的子類,實現(xiàn)了ObjectInput
接口,ObjectInput
是 DataInput
的子接口,增加了一個 readObject()
方法從流中讀取字節(jié)轉為對象。
序列化和反序列化的實質在于
ObjectOutputStream
的writeObject
和ObjectInputStream
的readObject
方法實現(xiàn),常見的String
、Date
、Double
、ArrayList
、LinkedList
、HashMap
、TreeMap
等都默認實現(xiàn)了Serializable
,.
有時候我們對象有些字段的值可能與內存位置(hashcode
)、當前時間等有關,所以我們不想序列化他(因為反序列化后的值是沒有意義的),或者有時候如果類中的字段表示的是類的實現(xiàn)細節(jié)而非邏輯信息則默認序列化也是不適合的,所以我們需要定制序列化,Java 提供的定制主要有transient
關鍵字方式和實現(xiàn) writeObject
、readObject
方式及 Externalizable
接口 readResolve
、writeReplace
方式,還可以將字段聲明為 transient
后通過 writeObject
、readObject
方法來自己保存該字段。
默認情況下 Java 會根據類中一系列信息自動生成一個版本號,在反序列化時如果類的定義發(fā)生了變化版本號就會變化,也就與反序列化流中的版本號不匹配導致會拋出異常,所以我們?yōu)榱烁玫目刂坪托阅軉栴}會自定義 serialVersionUID
版本號來避免類定義發(fā)生變化后反序列化版本號不匹配異常問題,如果版本號一樣時流中有該字段而類定義中沒有則該字段會被忽略,如果類定義中有而流中沒有則該字段會被設為默認值,如果對于同名的字段類型變了則會拋出 InvalidClassException
。
虛擬機是否允許反序列化不僅取決于類路徑和功能代碼是否一致,還取決于另一個非常重要的點是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L
)。
因為聲明實現(xiàn) Serializable
接口后保存讀取對象就可以使用 ObjectOutputStream
、ObjectInputStream
流了,ObjectOutputStream
的 writeObject(Object obj)
方法將對象轉化為字節(jié)寫到流中,ObjectInputStream
的 readObject()
方法從流中讀取字節(jié)轉為對象,Serializable
雖然是一個空接口,但是在調用 writeObject
方法時卻充當了一種健全的校驗作用,如果對象沒有實現(xiàn) Serializable
則在調用 writeObject
時就會拋出異常,所以說 Serializable
算是一種接口標識機制。
如下為 ObjectOutputStream
中 writeObject(Object obj)
的核心標記判斷:
private void writeObject0 (Object obj, boolean unshared) throws IOException {
try {
if(obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if(obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
} else if(obj instanceof String) {
writeString((String) obj, unshared);
} else if(cl.isArray()) {
writeArray(obj, desc, unshared);
} else if(obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if(obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if(extendedDebugInfo) {
throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
}
}