Effective Java(序列化问题)

同样是在第三条中提到的,对于单例模式下的序列化,反序列化生成新的对象的解决办法。我是没看懂的,于是在网上查了一些资料,才略知一二,仍然是从基础讲起,到实现反序列化后仍然保存单例的过程


1.定义

序列化:把对象转换为直接序列的过程称为对象的序列化

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


2.用途

<1>把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中

<2>在网络上传送对象的字节序列


3.序列化的过程(摘抄)

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
  对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。


4.具体的简单代码实现

要序列化的类,实现Serializable,给出的解释是相当于标记类可以序列化

import java.io.Serializable;

public class SerializClass implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1340334355599055794L;
	public static int age;
	private String name;
	private String sex;
	private int id;

	public int dd=45;
	
	
	public int getAge(){
		return age;
	}

	public String getName(){
		return name;
	}
	
	public String getSex(){
		return sex;
	}
	
	public int getId(){
		return id;
	}
	
	public void setAge(int age){
		this.age=age;
	}
	
	public void setName(String name){
		this.name=name;
	}
	
	public void setSex(String sex){
		this.sex=sex;
	}
	
	public void setId(int id){
		this.id=id;
	}
}

序列化

private static void SerializePerson() throws FileNotFoundException, IOException{
		serializClass=new SerializClass();
		serializClass.setName("gac1");
		serializClass.setAge(25);
		serializClass.setSex("男");
	//	serializClass.setId(1001);
		ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(new File("G:/new1/04eclipse/SerlizaClass.txt")));
		objectOutputStream.writeObject(serializClass);
		objectOutputStream.flush();
		objectOutputStream.close();
		System.out.println("序列化成功");
		
	}


反序列化

private static void DeserializePerson()throws FileNotFoundException,IOException{
		ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(new File("G:/new1/04eclipse/SerlizaClass.txt")));
		try {
			SerializClass serializClass=(SerializClass) objectInputStream.readObject();
			objectInputStream.close();
			System.out.println("name:"+serializClass.getName()+"\nAge:"+serializClass.getAge()+"\nSex"+serializClass.getSex());
			
			System.out.println(serializClass.dd);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

5.各种疑问

<1>对象序列化后,在进行反序列化,得到的对象和原对象是不相同的,反序列化会重新创建一个对象,然后将序列化的对象的一切拷贝给创建的对象

<2>对象序列化序列的是对象,而不是类,因此,对于static的属性或者方法是不会序列化的,因此反序列化后的对象输出的属性为空,在反序列化之前改变static属性的值,反序列化生成的对象中那个static属性的值会随之改变。transient关键字加在属性前,则该属性不会序列化

<3>serialVersionUID的作用,在程序中似乎并没有用到序列化,如果不声明,每编译一次会默认生成一次。

如果没有指定具体的值,当序列化后,修改SerializClass类中的任意代码,然后反序列化就会报错,serialVersionUID不匹配;但是指定serialVersionUID后,在序列化后修改,仍然可以反序列化,只不过修改的属性值反序列化后为空
<4>当反序列化时,如果父类没有实现序列化,会自动调用无参构造函数来构造父类对象,因此如果父类没有实现serializable接口,就必须有无参的构造函数。


6.解决问题

前面的介绍中已经知道了反序列化后的对象和之前的对象不是同一个,那么如何解决单例中实现序列化的问题呢?

其实就是简单的使用readResolve()方法,在其中返回单例对象,经过"=="测试后,返回true,如下所示:

public class SerializClass implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID =1L;
	private int age;
	private String name;
	private String sex;
	private int id;
	
	private static final SerializClass SERIALIZ_CLASS=new SerializClass();
/*
	private void writeObject(ObjectOutputStream out) throws IOException {
		// TODO Auto-generated method stub

		out.defaultWriteObject();
		out.writeInt(id);
	}
*/	
	
	public static SerializClass creatSer(){
		return SERIALIZ_CLASS;
	}
	//只用加上这个函数就够了
	private Object readResolve() {
		return creatSer();
	}
	
	private SerializClass(){
		//
	}
	
	public int getAge(){
		return age;
	}

	public String getName(){
		return name;
	}
	
	public String getSex(){
		return sex;
	}
	
	public int getId(){
		return id;
	}
	
	public void setAge(int age){
		this.age=age;
	}
	
	public void setName(String name){
		this.name=name;
	}
	
	public void setSex(String sex){
		this.sex=sex;
	}
	
	public void setId(int id){
		this.id=id;
	}
}

其实并不明白其中的原理,而且也查不到源码,找到了这样的一句话: 无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉。


好吧,就这样了!


参考:

http://www.cnblogs.com/xdp-gacl/p/3777987.html

http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html

http://www.2cto.com/kf/201405/305380.html

序列化与反序列化(百度文库)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值