Java序列化(Serialize)是指将一个Java对象写入IO流中; Java反序列化(Deserialize)指的是从IO流中回复IO对象。
序列化机制可以将Java对象转换为数据流用来保存在磁盘上或者通过网络传输。这使得对象可以脱离程序独立存在。
为了使对象支持序列化机制,需要让它的类变成可序列化的(serializable)。通过实现两个接口之一实现:
实现了Serializable接口的类,可以通过两个步骤序列化该对象:
//创建ObjectOutputStream输出流
ObjectOutputStream oos = new ObejctOutputStream(new FileOutputStream("object.txt"));
//将一个Person对象输出到输出流中
oos.writeObject(per);
public class Person implements java.io.Serializable {
private String name;
private int age;
// 这里没有无参构造器
public Person(String name, int age){
this.name = name;
this.age = age;
}
// name和age的setter和getter方法
...
}
import java.io.*;
public class Test{
public static void main(String[] args){
try{
// 创建ObjectOutputStream输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
Person per = new Person("Junzerg", 20);
// 将per对象写入输出流
oos.writeObject(per);
}
catch (IOException ex){
ex.printStackTrace();
}
}
}
AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E 2A 98 15 B9 5C 2E C1 6C 02 00 02 49 00 03 61 67 65 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00
14 74 00 07 4A 75 6E 7A 65 72 67
共75字节。
把Person类的age属性设置为Long,重新序列化,结果为:
AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E BE CF 78 98 E0 A3 0B E9 02 00 02 4A 00 03 61 67 65 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00 00 00 00 00 14 74 00 07 4A 75 6E 7A 65 72 67
共79字节。
// 出啊构建一个ObjectInputStream输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
//从输入流中读取一个Java对象并将其强制类型转换成Person类
Person p = (Person) ois.readObject();
从前文创建的Object.txt中读取Person类
import java.io.*;
public class ReadObject{
public static void main(String[] args){
try{
// 创建一个ObjectInputStream输入流
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("Object.txt"));
// 从输入流中读取一个Java对象,转换为Person类
Person p = (Person)ois.readObject();
System.out.println("name: " + p.getName()
+ "\n age: " + p.getAge());
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
name: Junzerg age: 20
如果要序列化的类的某个成员变量是一个非String类型的引用类型,那么这个引用类型必须是可序列化的。 例如有一个Teacher类持有Person类的引用
public class Teacher implements Seializable{
private String name;
Private Person student;
public Teacher(String name, Person student){
this.name = name;
this.student = student;
}
// 省略name和student的stter和getter方法
...
}
为了在反序列化Teacher对象时正确恢复,Person类必须也是可序列化的,否则Teacher不可序列化
当两个Teacher对象引用同一个Person对象的时候:
Person per = new Person("Junzerg", 20);
Teacher t1 = new Teacher("Miss Li", per);
Teacher t2 = new Teacher("Mr Wu", per);
在程序依次序列化三个对象的过程中,看起来似乎会向输出流中输出三个Person对象。 这时当程序从输入流中反序列化这些对象时,就会得到三个Person对象,这样这样t1和t2引用的就不是同一个Person对象了。
为了避免5.2中出现的错误,Java的序列化算法如下:
当对某个对象及进行序列化时,系统自动把该对象的所有实例变量依次进行序列化,如果某个实例变量引用另一个对象,则被引用的变量也会被序列化,这种情况被称为递归序列化。
在递归序列化的过程中,可能遇到不想被序列化或者不能被序列化的变量。这时可以使用transient关键字在序列化时忽略该变量,避免引发java.io.NotSerializableException异常。
public class Person implements java.io.Serializable {
private String name;
private transient int age;
// 这里没有无参构造器
public Person(String name, int age){
this.name = name;
this.age = age;
}
// name和age的setter和getter方法
...
}
注意:transient关键字只能用于修饰实例变量,不可修饰Java程序中的其他部分。
public class TransientTest{
public static void main(String[] args){
try{
// 创建ObjectOutputStream输出流
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("transient.txt"));
//创建ObjectInputStream输入流
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("transient.txt"));
Person per = new Person("Junzerg", 20);
// 将per对象序列化输出
oos.writeObject(per);
Person p = (Person) ois.readObject();
System.out.println(p.getAge());
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
0
3.2 transient.txt内容为:
AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E DB F9 DD 8C 83 99 C5 2E 02 00 01 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 74 00 07 4A 75 6E 7A 65 72 67
大小为65字节。
可以看到per实例中的age变量并没有序列化。