对象序列化与反序列化

        对象序列化的目标是将对象保存到磁盘中,或允许网络中直接传输对象。对象序列化允许把内存中的Java对象转换为平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。而其它程序获得了这种二进制流,都可以用反序列化将二进制流恢复成原来的Java对象。

1.使用对象流实现序列化

        如果需要将某个对象序列化,这个类应该实现Serializable接口或者Externalizable接口之一。这里首先介绍实现Serializable接口,只需要待序列化的类实现该接口即可。可看如下程序:

Person类:

import java.io.Serializable;

public class Person implements Serializable {
	public int age;
	public String name;
	
	public Person(int age,String name) {
		this.age=age;
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	

}

序列化:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Test1 {

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		//序列化
		
		Person person = new Person(11, "Bob");
		ByteArrayOutputStream os = new ByteArrayOutputStream();//定义一个字节数组输出流
		ObjectOutputStream outputStream = new ObjectOutputStream(os);//对象输出流
		outputStream.writeObject(person);//将对象写入到字节数组输出,实现序列化。
		byte[] personByte = os.toByteArray();
		System.out.println(Arrays.toString(personByte));

	}

}

返回这个序列化后的字节数组:

[-84, -19, 0, 5, 115, 114, 0, 15, 115, 101, 114, 105, 97, 98, 108, 101, 46, 80, 101, 114, 115, 111, 110, -84, -127, 59, -107, 84, -82, -111, -29, 2, 0, 2, 73, 0, 3, 97, 103, 101, 76, 0, 4, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 0, 0, 0, 11, 116, 0, 3, 66, 111, 98]

2.反序列化

        将以上序列化之后再进行反序列化,恢复Java对象,代码如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Test {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		//序列化
		
		Person person = new Person(11, "Bob");
		ByteArrayOutputStream os = new ByteArrayOutputStream();//定义一个字节数组输出流
		ObjectOutputStream outputStream = new ObjectOutputStream(os);//对象输出流
		outputStream.writeObject(person);//将对象写入到字节数组输出,实现序列化。
		byte[] personByte = os.toByteArray();
		System.out.println(Arrays.toString(personByte));
		
		
		//反序列化
		ByteArrayInputStream is = new ByteArrayInputStream(personByte);//字节组输入流
		ObjectInputStream inputStream = new ObjectInputStream(is);
		Person person2 = (Person)inputStream.readObject();
		System.out.println(person2.name+":"+person2.age);
		
		
		
		
		

	}

}

运行结果:

[-84, -19, 0, 5, 115, 114, 0, 15, 115, 101, 114, 105, 97, 98, 108, 101, 46, 80, 101, 114, 115, 111, 110, -84, -127, 59, -107, 84, -82, -111, -29, 2, 0, 2, 73, 0, 3, 97, 103, 101, 76, 0, 4, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 0, 0, 0, 11, 116, 0, 3, 66, 111, 98]
Bob:11

3.序列化文件存储唯一性

        即一个类多次序列化,但只有一个其对应的序列化存储区,无论其被多少个对应引用,都只有一个结果 。具体示例如下,首先将两个相同Person对象写入文件,然后反序列化出两个对象,进行比较:


import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Test1 {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		//序列化
		
		Person person = new Person(11, "Bob");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
		oos.writeObject(person);
		oos.writeObject(person);
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
		Person p1 = (Person)ois.readObject();
		Person p2 = (Person)ois.readObject();
		System.out.println(p1==p2);

	}

}

运行结果:

true

        以上结果说明:如果多次序列化同一个Java对象时,只有第一次序列化时才会把该Java对象转换成字节序列并输出。但这也有一定问题,当程序序列化一个可变对象时,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,当程序再次调用writeObject()方法时,程序只是输出前面的序列化编号,即使后面该对象的实例变量已被改变,改变的实例变量也不会输出。例子如下:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Test1 {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		//序列化
		
		Person person = new Person(11, "Bob");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
		oos.writeObject(person);
		person.setAge(12);
		oos.writeObject(person);
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
		Person p1 = (Person)ois.readObject();
		
		Person p2 = (Person)ois.readObject();
		System.out.println(p2.age);
		System.out.println(p1==p2);

	}

}

运行结果:

11
true

以上结果说明,即使age变量发生了改变,但两次person对象只认准第一次writeObject()方法的序列化结果。

4.关键字transient

        transient修饰的实例变量在序列化过程中将被完全隔离在序列化机制之外,从而导致在反序列化恢复Java对象时无法取得该实例变量值。如下例子所示:

import java.io.Serializable;

public class Person implements Serializable {
	public int age;
	public String name;
	public transient int height;
	
	public Person(int age,String name) {
		this.age=age;
		this.name = name;
	}
	public Person(int age,String name,int height) {
		this.age = age;
		this.name = name;
		this.height = height;
	}
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	

}

 


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class TransientTest {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("transient.txt"));
		Person person = new Person(11,"Tom",180);
		oos.writeObject(person);
		
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("transient.txt"));
		Person person2 = (Person)ois.readObject();
		System.out.println(person2.age);
		System.out.println(person2.height);

	}

}

运行结果如下:

11
0

5.自定义序列化机制-Externalizable接口

        该接口继承了Serializable接口,并且扩展了自定义序列化类的功能。

从下图可以看出,实现Externalizable接口,需要实现两个方法:readExternal()和writeExternal(),分别对应着序列化类的反序列化和序列化操作。

        根据源码上注释可知:

void writeExternal(ObjectOutput out) throws IOException:需要序列化的类实现writeExternal()方法来保存对象的状态。该方法调用DataOutput的方法来保存基本类型的实例变量值,调用ObjectOutput的writeObject()方法来保存引用类型的实例变量值。

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException:需要序列化的类实现该方法来实现反序列化。该方法调用DataInput的方法来恢复基本类型的实例变量值,调用ObjectInput的readObject()方法来恢复引用类型的实例变量值。

以下是代码示例:

Person类:

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import com.sun.org.apache.bcel.internal.generic.NEW;

public class Person implements Externalizable {
	public String name;
	public int age;
	
	public Person() {}
	public Person(String name,int age) {
	
		this.name =name;
		this.age =age;
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	//通过该方法来保存对象的状态。该方法调用DataOutput的方法来保存基本类型的实例变量值,调用ObjectOutput的writeObject()方法来保存引用类型的实例变量值,调用ObjectOutput的writeObject()方法来保存引用类型的实例变量值
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeObject(new StringBuilder(name).reverse());
		out.writeInt(age);

	}

	//需要序列化的类实现该方法来实现反序列化。该方法调用DataInput的方法来恢复基本类型的实例变量值,调用ObjectInput的readObject()方法来恢复引用类型的实例变量值
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		this.name = ((StringBuilder)(in.readObject())).reverse().toString();
		this.age = in.readInt();

	}

}

 测试类:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;



public class Test {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		//序列化
		Person person = new Person("Bob",12);
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
		oos.writeObject(person);
		person.setAge(13);
		oos.writeObject(person);
		
		
		//反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
		Person person2 = (Person)ois.readObject();
		Person person3 = (Person)ois.readObject();
		System.out.println(person2.name+":"+person2.age);
		System.out.println(person2==person3);
		
		
		
 		

	}

}

运行结果:

Bob:12
true

注意:实现Externalizable接口的类反序列化时,程序会先使用public的无参数构造器创建实例,然后执行readExternal()方法进行反序列化。因此实现Externalizable接口的类必须提供一个public的无参数构造器。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值