java.io.Serializable浅析

 Java API中java.io.Serializable接口源码:java

1 public interface Serializable {2 }

  类经过实现java.io.Serializable接口能够启用其序列化功能。未实现次接口的类没法使其任何状态序列化或反序列化。可序列化类的全部子类型自己都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。web

  Java的"对象序列化"能让你将一个实现了Serializable接口的对象转换成byte流,这样往后要用这个对象时候,你就能把这些byte数据恢复出来,并据此从新构建那个对象了。数据库

  要想序列化对象,你必须先建立一个OutputStream,而后把它嵌进ObjectOutputStream。这时,你就能用writeObject()方法把对象写入OutputStream了。数组

  writeObject()方法负责写入特定类的对象的状态,以便相应的 readObject()方法能够还原它。经过调用 out.defaultWriteObject 能够调用保存 Object 的字段的默认机制。该方法自己不须要涉及属于其超类或子类的状态。状态是经过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。安全

  读的时候,你得把InputStream嵌到ObjectInputStream里面,而后再调用readObject()方法。不过这样读出来的,只是一个Object的reference,所以在用以前,还得先下传。readObject() 方法负责从流中读取并还原类字段。它能够调用 in.defaultReadObject 来调用默认机制,以还原对象的非静态和非瞬态字段。  defaultReadObject()方法使用流中的信息来分配流中经过当前对象中相应命名字段保存的对象的字段。这用于处理类发展后须要添加新字段的情形。该方法自己不须要涉及属于其超类或子类的状态。状态是经过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。网络

  在序列化时,有几点要注意的:socket

  1:当一个对象被序列化时,只保存对象的非静态成员变量(包括声明为private的变量),不能保存任何的成员方法和静态的成员变量。this

  2:若是一个对象的成员变量是一个对象,那么这个对象的数据成员也会被序列化。spa

  3:若是一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操做将会失败,而且会抛出一个NotSerializableException。咱们能够将这个引用标记为transient,那么对象仍然能够序列化。code

 

  一、序列化是干什么的?

  简单说就是为了保存在内存中的各类对象的状态,而且能够把保存的对象状态再读出来。虽然你能够用你本身的各类各样的方法来保存Object States,可是Java给你提供一种应该比你本身好的保存对象状态的机制,那就是序列化。

  二、什么状况下须要序列化

  a)当你想把的内存中的对象保存到一个文件中或者数据库中时候;
  b)当你想用套接字在网络上传送对象的时候;
  c)当你想经过RMI传输对象的时候;

  三、当对一个对象实现序列化时,究竟发生了什么?

  在没有序列化前,每一个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)好比:

1 Foo myFoo = new Foo();2 myFoo .setWidth(37);3 myFoo.setHeight(70);

  当经过下面的代码序列化以后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样之后又能够把它从文件中读出来,从新在堆中建立原来的对象。固然保存时候不只仅是保存对象的实例变量的值,JVM还要保存一些小量信息,好比类的类型等以便恢复原来的对象。

1 FileOutputStream fs = new FileOutputStream("foo.ser");2 ObjectOutputStream os = new ObjectOutputStream(fs);3 os.writeObject(myFoo);

  四、实现序列化(保存到一个文件)的步骤

  a)Make a FileOutputStream

  java 代码
  FileOutputStream fs = new FileOutputStream("foo.ser");

  b)Make a ObjectOutputStream

  java 代码
  ObjectOutputStream os = new ObjectOutputStream(fs);

  c)write the object

  java 代码
  os.writeObject(myObject1);
  os.writeObject(myObject2);
  os.writeObject(myObject3);

  d) close the ObjectOutputStream

  java 代码
  os.close();

  五、举例说明

 

复制代码

 1 public class Box implements Serializable { 2     private static final long serialVersionUID = -3450064362986273896L; 3      4     private int width; 5     private int height; 6      7     public static void main(String[] args) { 8         Box myBox=new Box(); 9         myBox.setWidth(50);10         myBox.setHeight(30);11         try {12             FileOutputStream fs=new FileOutputStream("F:\\foo.ser");13             ObjectOutputStream os=new ObjectOutputStream(fs);14             os.writeObject(myBox);15             os.close();16             FileInputStream fi=new FileInputStream("F:\\foo.ser");17             ObjectInputStream oi=new ObjectInputStream(fi);18             Box box=(Box)oi.readObject();19             oi.close();20             System.out.println(box.height+","+box.width);21         } catch (Exception e) {22             e.printStackTrace();23         }24     }25     26     public int getWidth() {27         return width;28     }29     public void setWidth(int width) {30         this.width = width;31     }32     public int getHeight() {33         return height;34     }35     public void setHeight(int height) {36         this.height = height;37     }38 }

复制代码

  六、相关注意事项

  a)当一个父类实现序列化,子类自动实现序列化,不须要显式实现Serializable接口;
  b)当一个对象的实例变量引用其余对象,序列化该对象时也把引用对象进行序列化;
  c)并不是全部的对象均可以序列化,至于为何不能够,有不少缘由了,好比:

  1.安全方面的缘由,好比一个对象拥有private,public等field,对于一个要传输的对象,好比写到文件,或者进行rmi传输 等等,在序列化进行传输的过程当中,这个对象的private等域是不受保护的。
  2. 资源分配方面的缘由,好比socket,thread类,若是能够序列化,进行传输或者保存,也没法对他们进行从新的资源分配,并且,也是没有必要这样实现。

 

  serialVersionUID

  序列化运行时使用一个称为 serialVersionUID 的版本号与每一个可序列化类相关联,该序列号在反序列化过程当中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。若是接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不一样,则反序列化将会致使  InvalidClassException。可序列化类能够经过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其本身的 serialVersionUID:

 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

  若是可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 全部可序列化类都显式声明 serialVersionUID 值,缘由是计算默认的 serialVersionUID 对类的详细信息具备较高的敏感性,根据编译器实现的不一样可能千差万别,这样在反序列化过程当中可能会致使意外的 InvalidClassException。所以,为保证 serialVersionUID 值跨不一样 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private修饰符显示声明 serialVersionUID(若是可能),缘由是这种声明仅应用于直接声明类 -- serialVersionUID 字段做为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,所以它们老是具备默认的计算值,可是数组类没有匹配 serialVersionUID 值的要求。 

相关文章
相关标签/搜索