首页 > 编程 > Java > 正文

Java对象序列化

2019-11-06 06:14:48
字体:
来源:转载
供稿:网友

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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表