Java序列化
序列化的含义、意义以及使用场景
- 序列化:将对象写入到IO流中
- 反序列化:从IO流中恢复对象
- 意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
- 使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现
Serializeable
接口。
普通序列化
Serializable接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的。
序列化步骤
-
步骤一:创建一个ObjectOutputStream输出流;
-
步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象。
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
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 "Person{" + "name=" + this.name + ",age=" + this.age + "}";
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("object.txt"));
Person person = new Person("Aike", 20);
System.out.println(person.toString());
// 将对象序列化到文件中
oos.writeObject(person);
} catch (Exception e) {
System.out.println(e);
}
}
}
反序列化步骤
步骤一:创建一个ObjectInputStream输入流;
步骤二:调用ObjectInputStream对象的readObject()得到序列化的对象。
import java.io.*;
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
System.out.println("反序列化调用(检测反序列化是否调用了构造函数)");
this.name = name;
this.age = age;
}
// 存在无参构造函数将会报错
// public Person() {
// System.out.println("反序列化调用(检测反序列化是否调用了构造函数)");
// }
public String toString() {
return "Person{" + "name=" + this.name + ",age=" + this.age + "}";
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
// 创建一个ObjectInputStream流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
Person brady = (Person) ois.readObject();
System.out.println(brady.toString());
} catch (Exception e) {
System.out.println(e);
}
}
}
反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。
成员是引用的序列化
如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的,否则会导致此类不能序列化。
import java.io.*;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Teacher implements Serializable {
private String name;
Person person;
public Teacher(String name, Person person) {
this.name = name;
this.person = person;
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"));
Person person = new Person("孙悟空", 9999);
Teacher teacher = new Teacher("唐三藏", person);
oos.writeObject(teacher);
} catch (Exception e) {
System.out.println(e);
}
}
}
java.io.NotSerializableException: Person
程序报错的原因是Person类的对象是不可序列化的,这导致了Teacher对象不可序列化。
同一对象序列化多次的机制
import java.io.*;
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 "Person{" + "name=" + this.name + ",age=" + this.age + "}";
}
}
class Teacher implements Serializable {
private String name;
Person person;
public Teacher(String name, Person person) {
this.name = name;
this.person = person;
}
public String toString(){
return "Teacher{"+"name="+this.name+this.person.toString()+"}";
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"));
Person person = new Person("孙悟空", 9999);
Teacher t1 = new Teacher("唐三藏", person);
Teacher t2 = new Teacher("菩提祖师", person);
oos.writeObject(t1);
oos.writeObject(t2);
System.out.println(t1.toString());
System.out.println(t2.toString());
System.out.println("-----------------------------------");
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("teacher.txt"));
Teacher t_1=(Teacher)ois.readObject();
Teacher t_2=(Teacher)ois.readObject();
System.out.println(t_1.toString());
System.out.println(t_2.toString());
} catch (Exception e) {
System.out.println(e);
}
}
}
从输出结果可以看出,Java序列化同一对象,并不会将此对象序列化多次得到多个对象。
Java序列化算法
-
所有保存到磁盘的对象都有一个序列化编码号
-
当程序试图序列化一个对象时,会先检查此对象是否已经序列化过,只有此对象从未(在此虚拟机)被序列化过,才会将此对象序列化为字节序列输出。
-
如果此对象已经序列化过,则直接输出编号即可。