首页 > 代码库 > java序列化接口Serializable的使用

java序列化接口Serializable的使用

来自:http://wang09si.blog.163.com/blog/static/170171804201373195046397/

首先要明白,什么是序列化?
我们经常看到一个实体类实现Serializable接口,这种用法就是序列化。目的是保存对象的状态,以便将它读取出来。
序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例
Java为我们提供一种比较好的保存对象状态的机制,那就是序列化。
也就是说,保存状态和读取状态的事java已经帮我们做了,我们只需调相应的方法就可以了。

序列化的什么特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。

其次要搞清楚,什么情况下需要实例化?
 1、当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
 2、当你想用套接字在网络上传送对象的时候;
 java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
 3、当你想通过RMI传输对象的时候;
 RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。


 最后,就是如何实现序列化的?
 在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如: 
 Foo  myFoo = new Foo();
 myFoo .setWidth(37);
 myFoo.setHeight(70);
 当通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。
 FileOutputStream fs = new FileOutputStream("foo.ser");
 ObjectOutputStream os = new ObjectOutputStream(fs);
 os.writeObject(myBox);
 os.close();
 
 ObjectInputStream in = new ObjectInputStream (new FileInputStream("foo.ser"));
 Box box = (Box) (in.readObject());
 System.out.println(box.toString());
 System.out.println(box.height);
 in.close();

完整例子:
package wzq.j2se.erializables;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Box implements Serializable {
 private int width;
 private int height;

 public void setWidth(int width) {
  this.width = width;
 }

 public void setHeight(int height) {
  this.height = height;
 }

 public static void main(String[] args) {
  Box myBox = new Box();
  myBox.setWidth(50);
  myBox.setHeight(30);
  try {
   FileOutputStream fs = new FileOutputStream("foo.ser");
   ObjectOutputStream os = new ObjectOutputStream(fs);
   os.writeObject(myBox);
   os.close();
   
   ObjectInputStream in = new ObjectInputStream (new FileInputStream("foo.ser"));
   Box box = (Box) (in.readObject());
   System.out.println(box.toString());
   System.out.println(box.height);
   in.close();
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
} // writeObject和readObject本身就是线程安全的,传输过程中是不允许被并发访问的。所以对象能一个一个接连不断的传过来



子类:

package wzq.j2se.erializables;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExtendsBox extends Box{
 int age;

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }
 
 public static void main(String[] args) {
  ExtendsBox eb = new ExtendsBox();
  eb.setAge(101);
  try {
   FileOutputStream fs = new FileOutputStream("abc");
   ObjectOutputStream os = new ObjectOutputStream(fs);
   os.writeObject(eb);
   os.close();
   
   ObjectInputStream in = new ObjectInputStream (new FileInputStream("abc"));
   ExtendsBox box = (ExtendsBox) (in.readObject());
   System.out.println(box.toString());
   System.out.println(box.getAge());
   in.close();
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}


首先要明白,什么是序列化?
我们经常看到一个实体类实现Serializable接口,这种用法就是序列化。目的是保存对象的状态,以便将它读取出来。
序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例
Java为我们提供一种比较好的保存对象状态的机制,那就是序列化。
也就是说,保存状态和读取状态的事java已经帮我们做了,我们只需调相应的方法就可以了。

序列化的什么特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。

其次要搞清楚,什么情况下需要实例化?
 1、当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
 2、当你想用套接字在网络上传送对象的时候;
 java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
 3、当你想通过RMI传输对象的时候;
 RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。


 最后,就是如何实现序列化的?
 在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如: 
 Foo  myFoo = new Foo();
 myFoo .setWidth(37);
 myFoo.setHeight(70);
 当通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。
 FileOutputStream fs = new FileOutputStream("foo.ser");
 ObjectOutputStream os = new ObjectOutputStream(fs);
 os.writeObject(myBox);
 os.close();
 
 ObjectInputStream in = new ObjectInputStream (new FileInputStream("foo.ser"));
 Box box = (Box) (in.readObject());
 System.out.println(box.toString());
 System.out.println(box.height);
 in.close();

完整例子:
package wzq.j2se.erializables;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Box implements Serializable {
 private int width;
 private int height;

 public void setWidth(int width) {
  this.width = width;
 }

 public void setHeight(int height) {
  this.height = height;
 }

 public static void main(String[] args) {
  Box myBox = new Box();
  myBox.setWidth(50);
  myBox.setHeight(30);
  try {
   FileOutputStream fs = new FileOutputStream("foo.ser");
   ObjectOutputStream os = new ObjectOutputStream(fs);
   os.writeObject(myBox);
   os.close();
   
   ObjectInputStream in = new ObjectInputStream (new FileInputStream("foo.ser"));
   Box box = (Box) (in.readObject());
   System.out.println(box.toString());
   System.out.println(box.height);
   in.close();
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
} // writeObject和readObject本身就是线程安全的,传输过程中是不允许被并发访问的。所以对象能一个一个接连不断的传过来

 

子类:

package wzq.j2se.erializables;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExtendsBox extends Box{
 int age;

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }
 
 public static void main(String[] args) {
  ExtendsBox eb = new ExtendsBox();
  eb.setAge(101);
  try {
   FileOutputStream fs = new FileOutputStream("abc");
   ObjectOutputStream os = new ObjectOutputStream(fs);
   os.writeObject(eb);
   os.close();
   
   ObjectInputStream in = new ObjectInputStream (new FileInputStream("abc"));
   ExtendsBox box = (ExtendsBox) (in.readObject());
   System.out.println(box.toString());
   System.out.println(box.getAge());
   in.close();
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

 

serialVersionUID作用:
       序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
有两种生成方式:
       一个是默认的1L,比如:private static final long serialVersionUID = 1L;
       一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
       private static final   long     serialVersionUID = xxxxL;

当你一个类实现了Serializable接口,如果没有定义serialVersionUID,Eclipse会提供这个提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会自动给定两种生成的方式。如果不想定义它,在Eclipse的设置中也
       可以把它关掉的,设置如下:
        Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==>
        Potential programming problems
        将Serializable class without serialVersionUID的warning改成ignore即可。

如果你没有考虑到兼容性问题时,就把它关掉,不过有这个功能是好的,只要任何类别实现了Serializable这个接口的话,如果没有加入serialVersionUID,Eclipse都会给你warning提示,这个serialVersionUID为了让该类别Serializable向后兼容。

如果你的类Serialized存到硬盘上面后,可是后来你却更改了类别的field(增加或减少或改名),当你Deserialize时,就会出现Exception的,这样就会造成不兼容性的问题。

但当serialVersionUID相同时,它就会将不一样的field以type的预设值Deserialize,可避开不兼容性问题。

java序列化接口Serializable的使用