java 对象序列化

对象序列化

序列化    :将java对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输。

反序列化:将字节序列转换成java对象。

对象序列化步骤

  • 需要序列化的对象所对应的类需要实现Serializable接口;
  • 创建一个ObjectOutputStream实例,ObjectOutputStream是一个处理流,需要建立在其他节点流的基础之上;
// FileInputStream为节点流
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
  • 调用ObjectOutputStream的writeObject()方法输出对象;
        // 将person对象写入object.txt文件中
        oos.writeObject(person);

示例如下:

定义一个Person类,该类实现Serializable接口

class Person implements java.io.Serializable {

    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {this.name = name;}
    public String getName() {return name;}
    public void setAge(int age) {this.age = age;}
    public int getAge() {return age;}

}

创建一个Person类的实例对象,并将该对象写入object.txt文件中。

    public static void main(String[] args) throws IOException {

        // FileInputStream为节点流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));

        Person person = new Person("Sunwukong", 500);
        // 将person对象写入object.txt文件中
        oos.writeObject(person);
    }

对象反序列化

反序列化:将字节序列转换成java对象。

对象反序列化步骤

  • 创建一个ObjectInputStream实例,ObjectInputStream是一个处理流,需要建立在其他节点流的基础之上;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
  • 调用ObjectInputStream的readObject()方法读取流中的对象,该方法返回一个Object类型的java对象,如果知道java对象的类型,可以将该对象强制类型转换成其真实类型;
Person person = (Person) ois.readObject();

 

示例如下:

恢复object.txt文件中存储的person对象。

    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        Person newPerson = (Person) ois.readObject();
        System.out.println(newPerson.getName());   // 输出Sunwukong
        System.out.println(newPerson.getAge());    // 输出500
    }

反序列化得到的对象是一个全新的对象,是一个重新建造的对象,与原对象不同。

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
        Person person = new Person("Sunwukong", 500);
        oos.writeObject(person);

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        Person newPerson = (Person) ois.readObject();
        // newPerson是新建的对象,与person不是同一个对象
        System.out.println(person == newPerson); // 输出false
    }

限制序列化的实例变量

在一些情况下,一个类的某些实例变量为敏感信息,不希望系统将这些实例变量序列化。

或某些实例变量不可被序列化。

通过在实例变量前使用transient关键字,可以指定java序列化时忽略这些实例变量。

如在Person类中的实例变量age前使用transient关键字修饰,Person实例对象序列化时会忽视该变量

class Person  {

    private String name;
    // transient修饰age变量
    // 对象序列化时会忽略该变量
    private transient int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {this.name = name;}
    public String getName() {return name;}
    public void setAge(int age) {this.age = age;}
    public int getAge() {return age;}

}
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        Person newPerson = (Person) ois.readObject();
        System.out.println(newPerson.getName());   // 输出Sunwukong
        // age变量没有序列化,反序列化得到的值为默认值
        System.out.println(newPerson.getAge());    // 输出0
    }

 

实例变量为引用类型时

该引用类型必须是可序列化的,否则拥有该实例变量的类是不可序列化的(除非使用transient修饰该实例变量)。

当对某个对象进行序列化时,系统自动把该对象的实例变量序列化,如果某个实例变量引用到另一个对象,则被引用的对象也会被序列化;

若果被引用对象的实例变量也引用了其他对象,则被引用的对象也会被实例化;

. . . . . . .

这种情况被称为递归序列化。

示例:

定义Teacher类,该类的一个实例变量student为引用类型Person。

class Teacher implements java.io.Serializable {
    private String name;
    // student为引用类型
    private Person student;

    public Teacher(String name, Person student) {
        this.name = name;
        this.student = student;
    }

    public void setName(String name) {this.name = name;}
    public String getName() {return name;}
    public void setStudent(Person student) {this.student = student;}
    public Person getStudent() {return student;}
}

创建Teacher类的实例对象,并将该对象序列化(写入object.txt文件中)

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
        Person person = new Person("Sunwukong", 500);
        Teacher teacher = new Teacher("Tangseng", person);
        // 将teacher对象写入object.txt时,
        // 同时会将teacher中的引用类型变量student所指向的person对象写入object.txt中
        oos.writeObject(teacher);

反序列化teacher对象,也可以得到person对象

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        Teacher newTeacher = (Teacher) ois.readObject();
        System.out.println(newTeacher.getName());   // 输出Tangseng
        Person newPerson = newTeacher.getStudent();
        System.out.println(newPerson.getName());    // 输出Sunwukong       
        System.out.println(newPerson.getAge());     // 输出500

多个对象的序列化

当多个对象序列化时,java序列化机制采用如下算法:

  • 所有保存到磁盘中的对象都有一个序列化编号;
  • 当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未序列化过,系统才会将该对象序列化;
  • 如果某个对象已经序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象。

示例:

创建一个Person类实例对象person;

创建两个Teacher类实例对象t1与t2,两个对象都包含person;

依次向object.txt文件中写入实例对象t1 、t2、person、t1

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
        Person person = new Person("Sunwukong", 500);
        Teacher t1 = new Teacher("Tangseng", person);
        Teacher t2 = new Teacher("Putizushi", person);
        // 将以上对象写入object.txt时,
        oos.writeObject(t1);
        oos.writeObject(t2);
        oos.writeObject(person);
        oos.writeObject(t1);


        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        // 依次读取object.txt中的对象
        Teacher newt1 = (Teacher)ois.readObject();
        Teacher newt2 = (Teacher)ois.readObject();
        Person newPerson = (Person) ois.readObject();
        Teacher obj = (Teacher)ois.readObject();

        System.out.println(newt1.getStudent() == newt2.getStudent());  // true
        System.out.println(newt1.getStudent() == newPerson);           // true
        System.out.println(newt1 == obj);                              // true
    }

上述程序只有3个对象被序列化:person、t1、t2。

oos.writeObject(t1);    // 序列化t1与person
oos.writeObject(t2); // 序列化t2,person已经被序列化,t2中存放的是person序列化编码
oos.writeObject(person);
// person已经被序列化,存放的是person序列化编码
oos.writeObject(t1);      // t1已经被序列化,存放的是t1序列化编码

所以,从object.txt中取出对象时

newPerson与 newt1、newt2中的person都是同一个对象

obj与newt1也是同一个对象。

其他

关于对象序列化有以下几点需要注意:

对象的类名、实例变量都会被序列化,方法、类变量(static修饰的成员变量)、transient实例变量都不会被序列化。

序列化对象的实例变量应当也是可序列化的,否则使用transient关键字修饰。

反序列化对象时必须有序列化对象的class文件。

反序列化读取对象,必须按实际写入顺序读取。

原文地址:https://www.cnblogs.com/deltadeblog/p/9463643.html