java序列化接口Serializable的使用实例



java序列化接口Serializable的使用实例。首先要明白,什么是序列化?

经常看到一个实体类实现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,可避开不兼容性问题。