Java对象序列化

来源:互联网 发布:照片扫描软件手机版 编辑:程序博客网 时间:2024/06/14 04:39

1.序列化与对象流

对象序列化可以将任何对象写出到流中,并在之后将其读回

要序列化的对象的类必须实现 Serializable接口,Serializable接口没有任何方法

class Foo implements Serializable{...}//写入流ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.dat"));Foo foo1 = new Foo(1);out.writeObject(foo1);//从流中读出ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.dat"));Foo foo1 = (Foo)in.readObject();


序列化的一个重要作用是通过网络将对象传送到另一台计算机,因为序列化用序列号代替内存地址,每个对象都有一个序列号

类中静态成员不会被序列化,因为静态成员属于类级别,序列化只是序列化对象,所以序列化写入的只有非瞬态非静态数据域

读取对象的顺序与写入时的顺序要一致

2.修改默认的序列化机制

某些数据域不想或不能被序列化,用 transient (瞬态)标记,如:

private transient int num;


序列化和反序列化中需要特殊处理的行为可以使用下列签名的方法(对某些数据域)

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;private void writeObject(ObjectOutputStream out) throws IOException;//例子public class LabelPoint implements Serializable{     private String label;     private transient Point2D.Double point;     ...     private void writeObject(ObjectOutputStream out) throws IOException     {          out.defaultWriteObject();          out.writeDouble(point.getX());          out.writeDouble(point.getY());     }     private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException     {          in.defaultReadObject();          double x = in.readDouble();          double y = in.readDouble();          point = new Point2D.Double(x,y);     }}


readObject和writeObject方法不关心超类数据和其他类的信息。readExternal和writeExternal方法对包括超类数据在内的整个对象的存储和恢复负责(接口不一样)

public class Foo implements Externalizable{     private int a;     private String b;     private Date c;     public void readExternal(ObjectInputStream in) throws IOException     {          a = in.readInt();          b = in.readString();          c = new Date(in.readLong());     }     public void writeExternal(ObjectOutputStream out) throws IOException     {          out.writeInt(a);               out.writeString(b);          out.writeLong(c.getTime());     }}

3.序列化单例和类型安全的枚举

类型安全的枚举和单例模式的类需要添加readResolve方法,因为此时默认的序列化机制不起作用。

readResolve方法在对象被序列化之后被调用,必须返回一个对象,此对象会成为之后readObject方法的返回值,如:

class Foo implements Serializable{     public static final Foo TEST1=new Foo(1);     public static final Foo TEST2=new Foo(2);     private int value;     private Foo(int v){value=v;}}//TEST1是单例Foo original = Foo.TEST1;ObjectOutputStream out = ...out.writeObject(foo1);out.close();//此时saved和original不是同一个对象,errorObjectInputStream in = ...Foo saved = (Foo)in.readObject();//Foo类中添加readResolveprotected Object readResolve() throws ObjectStreamException{     if(value==1) return Foo.TEST1;     if(value==2) return Foo.TEST2;     return null; //这一步不应该发生}

4.版本管理

类可以对其早期版本进行兼容,但必须有这个类早期版本的指纹,即 serialVersionUID,此静态数据成员被放在某个类,则序列化系统就可以读入这个类的对象的不同版本(这个类的所有较新版本都必须把 serialVersionUID 定义为与初始版本相同)

class Foo implements Serializable{     ...     public static final long serialVersionUID=-133423423L;}


如果这个类只有方法发生了变化,则读入新对象数据时不会有问题;如果数据域发生了变化,则可能有问题,对象流将尽力将流对象转换成这个类的当前版本,做法如下(不处理瞬态和静态数据域):

  • 对于数据域名字匹配,类型不匹配,不会转换
  • 流中对象具有当前版本没有的数据域,则忽略这些数据域
  • 当前版本具有流对象中没有的数据域,则这些新添加的数据域设置成他们的默认值,对象为null,数字为0,boolean为false

参考文献:Java核心技术卷2

0 0
原创粉丝点击