现在的位置: 首页 > 综合 > 正文

浅析Java序列化中的四个常见问题

2013年08月11日 ⁄ 综合 ⁄ 共 2989字 ⁄ 字号 评论关闭

        一、实现对象序列化的方法有哪些?

        Java序列化有两种实现方法,一种在类定义的时候实现Serializable接口,另一种是实现Externalizable接口。实现了这两种接口的类的对象便可以转换成字节流或从字节流恢复,不需要在类中增加任何代码。只有极少数情况下带需要定制代码保存或恢复对象状态。

        一般选择使用Serializable接口实现序列化,因为该接口不需要实现任何方法;而Externalizable接口定义了writeExternal()和readExternal()方法,实现该接口的类必须要实现这两种方法。

         二、如何自定义序列化和反序列化过程?

        在对象序列化的过程中,如果涉及一些敏感信息,例如银行卡账号、密码等,则需要考虑到保密和如何防止在传递过程中泄露的问题。使用transient关键字可以保护这些属性,但是这些属性将不会参与序列化过程。此时,可以考虑显示定制序列化和反序列化的过程,在序列化时加入加密、在反序列化时加入解密等操作,而不是使用默认的序列化方式。

        对象在序列化和反序列化时,是调用内部的writeObject()和readObject()方法实现的。writeObject()和readObject()方法的定义如下:

private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException

        注意:为了防止在对象序列化过程中调用默认的writeObject()和readObject()方法,必须将这两个方法设置为private.    

        如果显示定制序列化和反序列化过程,只需重写上述两个方法。如果需要在writeObject()方法中使用默认的序列化机制,只需要在writeObject()方法中调用out.defaultWriteObject()方法;同理,在readObject()方法使用默认的序列化机制时,只需在readObject()方法中调用in.defaultReadObject()方法。

        在自定义序列化和反序列化的过程中,对于简单类型的属性,必须使用默认的writeInt(int intValue)、readInt(int intValue)等方法对简单类型进行读写操作;而对于引用类型的属性,可以在writeObject(ObjectOutputStream out)和readObject(ObjectInputStream in)中使用readObject()和writeObject()方法进行读写。

        另外,在文件或者网络中读取属性时,读取的顺序必须与写入的顺序保持一致。

        三、在序列化类中添加serialVersionUID属性有什么作用?

        serialVersionUID是一个私有的静态final属性,用于表明类之间不同版本的兼容性,该属性不是必须的。当一个类实现了java.io.Serializable接口,但是没有显示定义一个Long类型的serialVersionUID属性,java序列化机制会根据编译的class自动生成一个serialVersionUID作为该类的序列化版本ID号,只有同一次编译生成的class才会生成相同的serialVersionUID。在反序列化过程中,JVM会把传来的字节流中的serialVersionUID与本地类的serialVersionUID进行比较,如果相同就认为版本一致,可以进行反序列化,否则就会出现序列化版本不一致的InvalidClassException异常。如果不希望通过编译来强制划分软件版本,即实现序列化接口的类能够兼容以前版本,只需显示地定义一个名为serialVersionUID、类型为Long的final属性,即可保证相同的版本号,且在进行序列化和反序列化时不会出现InvalidClassException异常。

        四、当序列化遭遇继承时,如何正确处理对象序列化过程?

        在序列化过程中,如果父类实现了Serializable和Externalizable接口,则其所有子类都将自动实现序列化。

        但是,在实际开发中,这是不可取的。因为父类不能强制其所有子类都具有序列化的能力。

        如果父类没有序列化,而子类却实现了Serializable接口,那么子类对象也不能序列化。否则,会抛出InvalidClassException异常。

        下面,提供一种可行的方法,供大家参考:

        第一、编写一个能够实现序列化的子类,首先将自身序列化。

        第二、为其父类提供一个无参的构造方法并序列化父类的属性。

public abstract class AbsSuperClass {
	
	protected int superValue;
	
	/**
	 * 父类无参的构造方法
	 */
	public AbsSuperClass() {
		
	}
	
	public AbsSuperClass(int superValue) {
		this.superValue = superValue;
	}

	public int getSuperValue() {
		return superValue;
	}

	public void setSuperValue(int superValue) {
		this.superValue = superValue;
	}

	@Override
	public String toString() {
		return "superValue = " + superValue;
	}
	
}

/**
 * 定义子类并显示定义序列化和反序列化过程
 * @author gsucbiao
 *
 * @date 2011-9-7 && 上午12:23:35
 */
public class SubClass extends AbsSuperClass implements Serializable {

	private static final long serialVersionUID = 1L;

	private int subValue;
	
	public SubClass(int superValue, int subValue) {
		super(superValue);
		this.subValue = subValue;
	}
	
	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
		out.writeInt(superValue);
	}
	
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
		in.defaultReadObject();
		superValue = in.readInt();
	}

	@Override
	public String toString() {
		return super.toString() + "subValue = " + subValue;
	}
	
}

抱歉!评论已关闭.