serialized接口是标记接口,在jvm中是如何执行他的

来源:互联网 发布:json 值里面有双引号 编辑:程序博客网 时间:2024/05/29 16:24

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

Java代码 复制代码
  1. public class FooImpl implements java.io.Serializable{      
  2. private String message;      
  3.       
  4. public String getFoo() {              
  5.     return message;      
  6. }      
  7.       
  8. public void setMessage(String message) {      
  9.     this.message = message;      
  10. }      
  11.       
  12. private void writeObject(java.io.ObjectOutputStream out) throws IOException {      
  13.     System.out.println("writeObject invoked");      
  14.     out.writeObject(this.message == null ? "hohohahaha" : this.message);      
  15. }      
  16.       
  17. private void readObject(java.io.ObjectInputStream in) throws IOException,      
  18.         ClassNotFoundException {      
  19.     System.out.println("readObject invoked");      
  20.     this.message = (String) in.readObject();      
  21.     System.out.println("got message:" + message);      
  22. }      
  23.       
  24. private Object writeReplace() throws ObjectStreamException {      
  25.     System.out.println("writeReplace invoked");      
  26.     return this;      
  27. }      
  28.       
  29. private Object readResolve() throws ObjectStreamException {      
  30.     System.out.println("readResolve invoked");      
  31.     return this;      
  32. }      
  33.       
  34. public Object serialize() throws IOException, ClassNotFoundException {      
  35.     ByteArrayOutputStream baos = new ByteArrayOutputStream();      
  36.     ObjectOutputStream oos = new ObjectOutputStream(baos);      
  37.     oos.writeObject(this);      
  38.     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());      
  39.     ObjectInputStream ois = new ObjectInputStream(bais);      
  40.     return ois.readObject();      
  41. }      
  42.         public static void main(String[] args) throws IOException,       
  43.              ClassNotFoundException {      
  44.                  FooImpl fooimpl = new FooImpl();      
  45.                  fooimpl.serialize();      
  46. }    


我们运行这段代码看到的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代码 复制代码
  1.    public class FooImpl implements java.io.Externalizable {      
  2.     private String message;      
  3.       
  4.     public String getFoo() {      
  5.         return message;      
  6.     }      
  7.       
  8.     public void setMessage(String message) {      
  9.         this.message = message;      
  10.     }      
  11.       
  12.     private Object writeReplace() throws ObjectStreamException {      
  13.         System.out.println("writeReplace invoked");      
  14.         return this;      
  15.     }      
  16.       
  17.     private Object readResolve() throws ObjectStreamException {      
  18.         System.out.println("readResolve invoked");      
  19.         return this;      
  20.     }      
  21.       
  22.     public Object serialize() throws IOException, ClassNotFoundException {      
  23.         ByteArrayOutputStream baos = new ByteArrayOutputStream();      
  24.         ObjectOutputStream oos = new ObjectOutputStream(baos);      
  25.         oos.writeObject(this);      
  26.         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());      
  27.         ObjectInputStream ois = new ObjectInputStream(bais);      
  28.         return ois.readObject();      
  29.     }      
  30.       
  31.     public void readExternal(ObjectInput arg0) throws IOException,      
  32.             ClassNotFoundException {      
  33.         System.out.println("readExternal invoked");      
  34.         Object obj = arg0.readObject();           
  35.     }      
  36.       
  37.     public void writeExternal(ObjectOutput arg0) throws IOException {      
  38.         System.out.println("writeExternal invoked");      
  39.         arg0.writeObject("Hello world");      
  40.     }      
  41.          public static void main(String[] args) throws IOException,       
  42.               ClassNotFoundException {      
  43.                   FooImpl fooimpl = new FooImpl();      
  44.                   fooimpl.serialize();      
  45.     }      
  46. }    



我们运行这段代码看到的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中的对象的则不同。

原创粉丝点击