JAVA序列化基础知识Serializable与Externalizable

大家都知道Serializable是一个mark interface,告诉JVM这个对象可以被转换成二进制流来传输.
但是Serializable与Externalizable的转换二进制流的过程是不一样的.
Serializable 在我们实现这个接口的时候,我们可以使用4个私有方法来控制序列化的过程:
  我们来看一个例子:
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:
   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序列化的内容.



1.关键字transient
当一个成员被声明为transient时,在对象被保存的时候,该成员将不被保存。

2. 通过实现了Externalizable接口来产生Serialize功能
1)    在保存对象时不会保存任何成员对象。但我们可以手工保存成员对象(下面将讲到)。
2)    Default构造函数必须为public。因为在次第读取对象时,会调用default构造函数。因为对象中的成员对象不一定会被保存,所以要通过构造函数来进行初始化。
3)    Extrenalizable接口有writeExternal(ObjectOutput)和readExternal(ObjectInput)两个函数。在对实现了Externalizable接口的类进行次第读写时,会调用这两个函数。但是在恢复对象时,是先调用default构造函数再调用readExternal()函数的。

3.  Externalizable接口和Serializable接口的区别
1)    通过实现Serializable接口的方法,在保存对象时会把对象中所包含的成员对象也保存下来;而通过实现Externalizable接口的方法,在保存对象时不会保存任何成员对象。
2)    通过实现Serializable接口的方法,在恢复对象时不会调用任何构造函数(包括default构造函数);而通过实现Externalizable接口的方法,在恢复对象时会调用default构造函数。然后再调用readExternal()函数。
3)    利用writeExternal()和readExternal()函数来控制成员对象

4.  Java的怪异之处
1)    在下面的代码中我们可以看到,Blip3只实现了Serializable接口,而该接口只是个标志接口,所以这两个函数并不是接口的一部分。
2)    这两个函数被声明为private,按理只能在它们所在的类中被调用,但事实却是它们能被ObjectOutputStream和ObjectInputStream对象的writeObject()和readObject()函数调用。
3)    当调用ObjectOutputStream.writeObject(Object)时,传入的Serializable对象会被检查是否实现自己的writeObject(ObjectOutputStream)。如果有,就会执行扩增的writeObject(ObjectOutputStream)函数。当调用ObjectInputStream.readObject()时的工作过程也一样。
4)   在扩增的writeObject(ObjectOutputStream)中我们可以通过调用defaultWriteobject()函数来执行缺省的writeObject();在扩增的readObject(ObjectInputStream)函数中,我们可以通过调用defaultReadObject()函数来执行缺省的readObject()。在相应的缺省函数中都只对non-transient成员对象进行操作。
5.  关于Serialize的扩充话题
1)   当多个对象被保存到同一个Stream时,如果这些对象的成员对象中有指向相同的第三对象的reference,那么在恢复时也将指向同一个对象。但不相同的两个Stream中的对象的则不同。

你可能感兴趣的:(java,jvm)