Java对象序列化

        当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

  • 把Java对象转换为字节序列的过程称为对象的序列化
  • 把字节序列恢复为Java对象的过程称为对象的反序列化

        Java序列化提供两个接口Serializable,Externalizable。一个类实现了两者接口中任何一个都表示该类为可序列化类。

        类通过实现 java.io.Serializable 接口以启用其序列化功能。该接口是一个标识接口,没有方法或字段。实现该接口的类,JVM实现默认的序列化和反序列化。

需要特殊处理的序列化类则需使用下列签名的方法:

 1). private void writeObject(ObjectOutputStream out) throws IOException;

       负责写入类的对象的状态,以便相应的readObject方法可以反序列化恢复它。通过调用out.defaultWriteObject将当前类的非静态和非瞬态字段写入流中。out.defaultWriteObject是上述讲到的序列化默认机制。out还提供其他方法写入流中,如writeInt(int val),writeUTF(String str)。
 2). private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

       负责从流中读取并恢复类字段。它可以调用 in.defaultReadObject 来调用默认机制,以恢复对象的非静态和非瞬态字段。
 3). private void readObjectNoData() throws ObjectStreamException;

       负责正确初始化特定的类对象状态。在反序列化时,序列化流里未列出反序列化对象的超类(即出现继承关系发生变化情形)时,如果超类提供了readObjectNoData方法,则反序列化时将引发调用。通常是用于初始化超类中的field。
 4). ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

      用来重新指定被序列化的对象,即该方法返回的对象才是真正要被序列化的对象。
 5). ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

      用来重新指定反序列化得到的对象。可在单例反序列化时候使用。

注意:

         ①.我们认为反序列化也是构建对象的一种方式,类似构造函数。只是参数不同,反序列化使用的是字节流做为参数。

         ②.实现Serializable接口并反序列化时,不会调用类的构造函数来构建对象,因此通过反序列化构建的对象可能会违反约束条件。

         ③.如果父类是不可序列化的,但子类实现了Serializable 接口,则父类必须提供默认的无参构造函数,否则子类不可序列化,将引发java.io.InvalidClassException异常。代码如下:

public class SuperClass {
	private int x;
	public SuperClass(int x) {
		this.x = x;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
}
public class SubClass extends SuperClass implements Serializable {
	private int y;
	public SubClass(int y){
		super(-1);
		this.y = y;
	}
	public static void main(String[] args) {
		SubClass o_obj = new SubClass(2);
		o_obj.setX(1);
		byte serializedObject[] = null;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(bos);
			out.writeObject(o_obj);
			out.close();
			serializedObject = bos.toByteArray();
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedObject));
			SubClass new_obj = (SubClass) in.readObject();
			print(new_obj);
			bos.close();
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public String toString(){
		return "("+this.getX()+","+this.getY()+")";
	}
	public static void print(Object obj) {
		System.out.println(obj.toString());
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
}
执行后引发异常,Caused by: java.io.InvalidClassException: com.andy.serial.SubClass; no valid constructor

④.上述父类提供默认构造函数后,默认序列化机制是不会对父类自动序列化的,子类需要自定义实现writeObject,readObject方法。

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
		out.write(this.getX());
	}
	
	private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {
		in.defaultReadObject();
		this.setX(in.readInt());
	}

⑤.父类field一般是有业务约束的并非是Java的默认值。这时候需要父类提供初始化方法如init(),且init方法一般携带参数,类似有参构造函数。

init方法有两大功能:a.为反序列化提供类似有参的构造函数功能以完成状态初始化,b.实现业务约束的校验,以防止不符合业务约束的对象生成。

Externalizable接口,若要完全控制某一对象及其超类的流格式和内容则实现该接口。接口中方法签名如下

        void writeExternal(ObjectOutput out) throws IOException;

        void readExternal(ObjectInput in) throws IOException,ClassNotFoundException;

        注意参数与之前的方法参数不用。如果类实现了Externalizable接口则优先使用这两个方法。

       ⑥. 在使用Externalizable接口反序列化时,会先调用类的不带参数的修饰符为public的构造函数再反序列化。与之前的默认序列化机制有所区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值