一、对象序列化的基本概念
所谓的对象序列化(在某些书籍中也叫串行化),是指在内存之中保存的对象转化为二进制数据流的形式的一种操作。通过将对象序列化,可以方便地实现对象的传输及保存。
但是在Java之中并不是所有的类的对象都可以被序列化,如果一个类对象需要被序列化,则此类一定要实现java. io.Serializable接口。但是这个接口里面也没有定义任何的方法,所以此接口依然属于标识接口,表示一种能力。
在Java中提供有ObjectlnputStream与ObjectOutputStream这两个类用于序列化对象的操作。这两个类是用于存储和读取对象的输入输出流类,不难想象,只要把对象中的所有成员变量都存储起来,就等于保存了这个对象,之后从保存的对象之中再将对象读取进来就可以继续使用此对象。
ObjectInputStream与ObjectOutputStream类,用于帮助开发者完成保存和读取对象成员变量取值的过程,但要求读写或存储的对象必须实现了Serializable接口,但Serializable接口中没有定义任何方法,仅仅被用做一种标记,以被编译器做特殊处理。如下范例所示。
【示例1】
package com.xy.io;
import java.io.Serializable;
public class Person implements Serializable {
// Person实现了Serializable接口,所以此类定义的对象可被序列化
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ", 年龄:" + this.age;
}
}
二、对象输出流——ObjectOutputStream
虽然类已经实现了Serializable接口,但是如果要想真正地实现具体的序列化操作,则用户可以使用ObjectOutputStream类完成,这个类继承结构如下。
java.lang.Object
|- java.io.OutputStream
|- java.io.ObjectOutputStream
由上述类的继承关系,可以发现ObjectOutputStream是OutputStream子类,因为对象序列化之后为二进制数据,所以只能够依靠字节流操作,同时在ObjectOutputStream类中定义了以下两个方法:
(1)构造方法。public ObjectOutputStream(OutputStream out) throws IOException;
(2)输出对象:public final void writeObject(Object obj) throws IOException;
ObjectOutputStream 用于将对象序列化,并保存。其操作如下。
OutputStream outputFile = new FileOutputStream(new File(“SerializedPerson”));
ObjectOutputStream cout = new ObjectOutputStream(outputFile);
cout.writeObject(new Person("张三",25));
cout.close();
ObjectOutputStream 接收一个OutputStream对象用于保存待序列化的对象。然后cout调用writeObject 方法保存对象。
三、对象输入流——ObjectInputStream
如果希望将已被序列化的对象再反序列化回来,则就可以通过ObjectInputStream类完成,它用于读取将序列化的对象。此类继承关系如下。
java.lang.Object
|- java.io.InputStream
|- java.io.ObjectInputStream
对于ObjectInputStream类之中主要使用的两个方法如下。
(1)构造方法。public ObjectInputStream(InputStream in) throws IOException;
(1)对象输入:public final Object readObject() throws IOException, ClassNotFoundException;
实现对象的反序列化(读取对象)操作如下。
InputStream inputFile = new FileInputStream(new File(“SerializedPerson”));
ObjectInputStream cin = new ObjectInputStream(inputFile);
Person p = (Person) cin.readObject();
System.out.println(p);
ObjectInputStream接收一个InputStream对象用于保存待序列化的对象。然后cin调用readObject方法读取序列化后的对象。
四、反序列化的基本概念
反序列化实际上就是使用ObjectInputStream 类创建对象将序列化后的对象读取出来,继续使用此对象。下面的例子结合ObjectInputStream 和OutputStream演示如何序列化对象和反序列化对象。
【示例2】
package com.xy.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
public class SerializableDemo {
public static void main(String[] args) throws Exception {
File f = new File("SerializedPerson");
serialize(f);
deserialize(f);
}
// 序列化对象方法,将对象保存在文件之中
public static void serialize(File f) throws Exception {
OutputStream outputFile = new FileOutputStream(f);
ObjectOutputStream cout = new ObjectOutputStream(outputFile);
cout.writeObject(new Person("张三", 25));
cout.close();
}
// 反序列化对象方法,从文件中读取已经保存的对象
public static void deserialize(File f) throws Exception {
InputStream inputFile = new FileInputStream(f);
ObjectInputStream cin = new ObjectInputStream(inputFile);
Person p = (Person)cin.readObject();
System.out.println(p);
cin.close();
}
}
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ", 年龄:" + this.age;
}
}
【结果】
五、transient关键字
在默认情况下,当一个类对象序列化时,会将这个类中的全部属性都保存下来,如果不希望类中的某个属性被序列化(或某些属性不希望被保存,则可以在声明属性之前加上transient关键字。下面的代码修改自 【示例2】 ,在声明属性时,前面多加了一个transient关键字。
private transient String name;
private transient int age;
再次运行 【示例2】 程序时,其输出结果如下图所示。
从输出结果可以看到,Person类中的两个属性并没有被保存下来,输出时,是直接输出了这两个属性的默认值null和0。