黑马程序员-----IO之对象流和对象序列化

时间:2022-03-06 11:30:53
---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

1.对象序列化的含义和意义
对象的序列化指将一个Java对象写入IO流中,与此对应的是,对象的反序列化则指从IO流中恢复该Java对象。
如果需要让某个对象可以支持序列化机制,必须让它的类是可序列化的,即该类必须实现如下两个接口之一:
--> Serializable
--> Externalizable
Java的很多类已经实现了Serializable,该接口是一个标记接口,实现该接口无须实现任何方法,它只是表明该类的实例是可序列化的。

对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象,对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。其他程序一旦获得了这种二进制流(无论是从磁盘上获取,还是通过网络获取),都可以将这种二进制流恢复成原来的Java对象。序列化机制使得对象可以脱离程序的运行而独立存在。


2.使用对象流实现对象序列化
对象流包括ObjectOutputStream和ObjectInputStream。

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。 其writeObject 方法用于将对象写入流中。

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。其readObject方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。 


一旦某个类实现了Serializable接口,则该类的对象就是可序列化的,程序可以通过如下两个步骤来序列化该对象。

1)创建一个ObjectOutputStream,这个输出流是一个处理流,所以必须建立在其他节点流的基础上。

2)调用ObjectOutputStream对象的writeObject方法输出可序列化对象。


下面程序定义了一个Person类,这个Person类就是一个普通Java类,只是实现了Serializable接口。

public class Person implements Serializable{

	//属性
	private String name;
	private int age;
	
	//构造器
	Person(){}
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	//setter和getter方法
	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;
	}
	
}

下面程序使用ObjectOutputStream将一个Person对象写入磁盘文件。

public class WriteObject {

	public static void main(String[] args) {

		ObjectOutputStream oos = null;
		try{
			//创建一个ObjectOutputStream输出流
			oos = new ObjectOutputStream(new FileOutputStream("e:\\object.txt"));
			Person p = new Person("郭靖", 24);
			//将对象写入输出流
			oos.writeObject(p);
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			try{
				if(oos != null)
					oos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}

}

运行看到生产一个Person.txt文件,该文件内容就是Person对象。


如果要从二进制流中恢复Java对象,则需要使用反序列化。反序列化的步骤如下:

1)创建一个ObjectInputStream,这个输入流是一个处理流,所以必须建立在其他节点流的基础上。

2)调用ObjectInputStream对象的readObject方法读取流中的对象,该方法返回值是Object类型,一般要进行强制类型转换。

public class ReadObject {

	public static void main(String[] args) {

		ObjectInputStream ois = null;
		try{
			//创建一个ObjectInputStream输入流
			ois = new ObjectInputStream(new FileInputStream("e:\\object.txt"));
			//从输入流中读取一个Java对象,并将其强制转换成Person类
			Person p = (Person)ois.readObject();
			System.out.println("名字是:" + p.getName() + ",年龄:" + p.getAge());
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try{
				if(ois != null)
					ois.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}

}

注意:

1)反序列化读取的仅仅是Java对象的数据,而不是Java类。因此采用反序列化恢复Java对象时,必须提供Java对象所属类的class文件,否则将会引发ClassNotFoundException。

2)反序列化机制无须通过构造器来初始化Java对象。

3)如果向文件中使用序列化机制写入了多个Java对象,使用反序列化机制恢复对象时必须按实际写入的顺序读取。

4)如果一个可序列化类有多个父类(包括直接或间接父类),则该类的所有父类要么是可序列化的,要么有无参数的构造器——否则会引发InvalidClassException异常。

因为当程序创建子类实例时,系统会隐式地为它的所有父类都创建实例(并建立和此子类实例的关联),当反序列化某个子类的实例时,反序列化机制需要恢复其关联的父类实例,恢复这些父类实例有两种方式:

a.使用反序列化机制。

b.使用父类无参数的构造器。

反序列化机制优先采用第一种机制。如果某个父类既不可序列化,则不能使用第一种机制;有没有提供无参数的构造器,则不可采用第二种机制,那么反序列化该子类实例时将抛出异常。




---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net