Java对象序列化
首先介绍一下对象序列化的目标:对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。
一、序列化的含义和意义:
序列化机制使得对象可以脱离程序的运行而独立存在。
对象的序列化指将一个Java对象写入IO流中,与此对应的反是,对象的反序列化则指从IO流中恢复该Java对象。
如果需要需要让某个对象支持序列化机制,则必须让它的类是可序列化的(serializable)。为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializable
- Externalizable
Serializable接口是一个标记接口,实现该接口无需实现任何方法,它只是表明该类的实例是可序列化的。
所有需要保存到磁盘里的对象的类都应该可序列化。
二、使用对象流实现序列化(使用Serializable接口)
使用 Serializable 来实现序列化非常简单,主要让目标类实现 Serializable 标记接口即可,无须实现任何方法。
- 一旦某个类实现了 Serializable 接口,该类的对象就是可序列化的,程序可以通过如下两个步骤来序列化该对象。
1.创建一个 ObjectOutputStream,这个输出流是一个处理流,所以必须建立在其他节点流的基础上。如下代码所示。
//创建 ObjectOutputStream 输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.txt));
2.调用 ObjectOutputStream 对象的writeObject()方法输出可序列化对象。如下代码所示。
//将一个对象输出到输出流中
oos.writeObject(text);
- 如果希望从二进制流中恢复 Java对象,则需要使用反序列化。反序列化的步骤如下.
1.创建一个 ObjectInputStream 输入流,这个输入流是一个处理流,所以必须建立在其他节点流的基础之上。如下代码所示。
//创建一个ObjectInputStream()输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputstream("file.txt"));
2.调用 ObjectInputStream 对象的readObject()方法读取流中的对象,该方法返回一个 Object 类型的 Java 对象,如果程序知道该 Java 对象的类型,则可以将该对象强制类型转换成其真实的类型。如下代码所示。
//从输入流中读取一个 Java 对象,并将起强制类型转换为 Test 类
Test t = (Test) ois.readObject();
三、实例
- 下面程序定义了一个 Student 类,这个 Student 就是一个普通的 Java 类,只是实现了 Serializable 接口,该接口标识该类的对象是可序列化的。
public class Student implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int num;
private String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 下面程序使用 ObjectOutputStream 将一个 Student 对象写入磁盘文件
public class WriteObjcect {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Student.txt"));
Student stu = new Student(1,"吴彦祖");
oos.writeObject(stu);
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面程序中的第四行代码创建了一个 ObjectOutputStream 输出流,这个 ObjectOutputStream 输出流建立在一个文件输出流的基础上;程序第六行代码使用 writeObject() 方法将一个 Student 对象写入输出流。运行上面程序,将会看到生成了一个 Student.txt 文件,该文件的内容就是 Student 对象。
- 下面程序示范了从刚刚生成的 Student.txt 文件中读取 Student 对象的步骤
public class ReadObject {
public static void main(String[] args) {
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Student.txt"));
Student stu = (Student) ois.readObject();
System.Out.println("名字为:" + p.getname() + "\n年龄为:" + p.getnum());
} catch(Exception e) {
e.printStackTrace();
}
}
}
上面程序第四行代码将一个文件输入流包装成 ObjectInputStream 输入流,第五行代码使用 readObject() 读取了文件中的 Java 对象,这就完成了反序列化过程。
四、注意
- 反序列化读取的仅仅是 Java 对象的数据,而不是 Java 类,因此采用反序列化恢复 Java 对象时,必须提供该 Java 对象所属的 class 文件,否则将会引发 ClassNotFoundException 异常。
- Student 类只有一个有参的构造器,没有无参的构造器,而且该构造器内有一个普通的打印语句。当反序列化 Java 对象时,并没有看到程序调用该构造器,这表明反序列化机制无须通过构造器来初始化 Java 对象。
- 如果使用序列化机制向文件中写入了多个 Java 对象,使用反序列化机制恢复对象时必须按实际写入的顺序读取。
- 当一个可序列化对象有多个父类时(包括直接父类和间接父类),这些父类要么有无参的构造器,要么也是可序列化的——否则反序列化时将抛出 InvalidClassException 异常。如果父类是不可序列化的,只是带有无参构造器,则该父类中定义的成员变量值不会序列化到二进制流中。
**end**