`

对象的序列化与反序列化

    博客分类:
  • java
阅读更多

序列化:把对象转换为字节序列的过程

反序列化:把字节序列恢复为对象的过程

 

什么情况下需要序列化:

1、当你想把的内存中的对象状态保存到一个文件中或者数据库中时候

2、当你想用套接字在网络上传送对象的时候

3、当你想通过RMI传输对象的时候

 

JDK类库中的序列化API

    java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

    java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可

以采用默认的序列化方式 。

  对象序列化包括如下步骤:

  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;

  2) 通过对象输出流的writeObject()方法写对象。

 

  对象反序列化的步骤如下:

  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;

  2) 通过对象输入流的readObject()方法读取对象。

 

示例:

public class PhoneNumber implements Serializable{
	private static final long serialVersionUID = 1L;
	private final short areaCode;
	private final short prefix;
	private final short lineNumber;
	
	public PhoneNumber(short areaCode, short prefix, short lineNumber) {
		this.areaCode = areaCode;
		this.prefix = prefix;
		this.lineNumber = lineNumber;
	}
	
	@Override
	public boolean equals(Object o) {
		if(o == this) {
			return true;
		}
		
		if(!(o instanceof PhoneNumber)) {
			return false;
		}
		
		PhoneNumber number = (PhoneNumber)o;
		
		return number.lineNumber == lineNumber &&
				number.prefix == prefix &&
				number.areaCode == areaCode;
	}
}

public class TestSerializable {
	public static void main(String[] args) throws Exception, IOException {
		serializable();
		PhoneNumber p = Deserializable();
		System.out.println(p);
	}
	
	private static void serializable() throws IOException {
		ObjectOutputStream ob = new ObjectOutputStream(new FileOutputStream("D://PhoneNumber.txt"));
		PhoneNumber p = new PhoneNumber((short)100,(short)101,(short)102);
		ob.writeObject(p);
	}
	
	private static PhoneNumber Deserializable() throws ClassNotFoundException, IOException {
		ObjectInputStream oi = new ObjectInputStream(new FileInputStream("D://PhoneNumber.txt"));
		PhoneNumber p = (PhoneNumber) oi.readObject();
		return p;
	}
}

 

序列化PhoneNumber成功后在D盘生成了一个PhoneNumber.txt文件,而反序列化PhoneNumber是读取D盘的PhoneNumber.txt后生成了一个PhoneNumber对象.

 

serialVersionUID的作用

s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。

类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。

 

显式地定义serialVersionUID有两种用途:

1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

 

如果将上例中的PhoneNumber的serialVersionUID注释掉,并且在PhoneNumber中添加一个childPhoneNUmber域,这个时候再去利用生成的PhoneNumber.txt反序列化PhoneNumber,也会报异常。但是如果解除serialVersionUID的注释,则可以正常反序列化。

将上面示例的serialVersionUID改为2L,这个时候再去利用生成的PhoneNumber.txt反序列化PhoneNumber,也会导致InvalidClassException。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics