Java对象序列化

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不是同一个对象,error
ObjectInputStream in = ...
Foo saved = (Foo)in.readObject();

//Foo类中添加readResolve
protected 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值