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