序列化Serializable和Externalizable的區別
大家都知道Serializable是一個mark interface,告訴JVM這個對象可以被轉換成二進制流來傳輸.
但是Serializable與Externalizable的轉換二進制流的過程是不一樣的.
Serializable 在我們實現這個接口的時候,我們可以使用4個私有方法來控制序列化的過程:
我們來看一個例子:
Java代碼
-
public class FooImpl implements java.io.Serializable{
-
private String message;
-
-
public String getFoo() {
-
return message;
-
}
-
-
public void setMessage(String message) {
-
this.message = message;
-
}
-
-
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
-
System.out.println("writeObject invoked");
-
out.writeObject(this.message == null ? "hohohahaha" : this.message);
-
}
-
-
private void readObject(java.io.ObjectInputStream in) throws IOException,
-
ClassNotFoundException {
-
System.out.println("readObject invoked");
-
this.message = (String) in.readObject();
-
System.out.println("got message:" + message);
-
}
-
-
private Object writeReplace() throws ObjectStreamException {
-
System.out.println("writeReplace invoked");
-
return this;
-
}
-
-
private Object readResolve() throws ObjectStreamException {
-
System.out.println("readResolve invoked");
-
return this;
-
}
-
-
public Object serialize() throws IOException, ClassNotFoundException {
-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
ObjectOutputStream oos = new ObjectOutputStream(baos);
-
oos.writeObject(this);
-
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-
ObjectInputStream ois = new ObjectInputStream(bais);
-
return ois.readObject();
-
}
-
public static void main(String[] args) throws IOException,
-
ClassNotFoundException {
-
FooImpl fooimpl = new FooImpl();
-
fooimpl.serialize();
-
}
我們運行這段代碼看到的debug信息:
writeReplace invoked
writeObject invoked
readObject invoked
readResolve invoked
當進行序列化的時候:
首先JVM會先調用writeReplace方法,在這個階段,我們可以進行張冠李戴,將需要進行序列化的對象換成我們指定的對象.
跟著JVM將調用writeObject方法,來將對象中的屬性一個個進行序列化,我們可以在這個方法中控制住哪些屬性需要序列化.
當反序列化的時候:
JVM會調用readObject方法,將我們剛剛在writeObject方法序列化好的屬性,反序列化回來.
然後在readResolve方法中,我們也可以指定JVM返回我們特定的對象(不是剛剛序列化回來的對象).
注意到在writeReplace和readResolve,我們可以嚴格控制singleton的對象,在同一個JVM中完完全全只有唯一的對象,控制不讓singleton對象產生副本.
Externalizable 是一個有實際方法需要實現的interface,包括writeExternal和readExternal:
Java代碼
-
public class FooImpl implements java.io.Externalizable {
-
private String message;
-
-
public String getFoo() {
-
return message;
-
}
-
-
public void setMessage(String message) {
-
this.message = message;
-
}
-
-
private Object writeReplace() throws ObjectStreamException {
-
System.out.println("writeReplace invoked");
-
return this;
-
}
-
-
private Object readResolve() throws ObjectStreamException {
-
System.out.println("readResolve invoked");
-
return this;
-
}
-
-
public Object serialize() throws IOException, ClassNotFoundException {
-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
ObjectOutputStream oos = new ObjectOutputStream(baos);
-
oos.writeObject(this);
-
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-
ObjectInputStream ois = new ObjectInputStream(bais);
-
return ois.readObject();
-
}
-
-
public void readExternal(ObjectInput arg0) throws IOException,
-
ClassNotFoundException {
-
System.out.println("readExternal invoked");
-
Object obj = arg0.readObject();
-
}
-
-
public void writeExternal(ObjectOutput arg0) throws IOException {
-
System.out.println("writeExternal invoked");
-
arg0.writeObject("Hello world");
-
}
-
public static void main(String[] args) throws IOException,
-
ClassNotFoundException {
-
FooImpl fooimpl = new FooImpl();
-
fooimpl.serialize();
-
}
-
}
我們運行這段代碼看到的debug信息:
writeReplace invoked
writeExternal invoked
readExternal invoked
readResolve invoked
在此writeExternal 和readExternal 的作用與writeObject和readObject 一樣.
最後,當我們同時實現了兩個interface的時候,JVM只運行Externalizable 接口裡面的writeExternal 和readExternal 方法對序列化內容進行處理.
需要注意的是:Serializable是一個真正的mark interface,
writeObject,readObject, writeReplace,readResolve是直接與JVM通信,告訴JVM序列化的內容.