📜  Java中带有继承的对象序列化(1)

📅  最后修改于: 2023-12-03 15:31:49.437000             🧑  作者: Mango

Java中带有继承的对象序列化

在Java中,对象序列化是将对象转换成字节流的过程。这个过程可以用来在不同的应用程序之间传递对象或者将对象存储到文件或数据库等持久化存储中。

当使用Java中带有继承的类进行对象序列化时,需要注意一些细节。

带有继承的类的序列化

考虑下面这个带有继承的类:

class Person implements Serializable {
    private String name;
    private int age;

    // constructors, getters, and setters
}

class Student extends Person implements Serializable {
    private String major;

    // constructors, getters, and setters
}

在这个例子中,Person是一个基类,Student是一个子类。

如果想要序列化一个Student对象,只需要将Student类声明为Serializable,Java序列化机制会自动序列化Person类中的字段。

Student student = new Student("Alice", 20, "Computer Science");

try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.ser"))) {
    oos.writeObject(student);
} catch (IOException e) {
    e.printStackTrace();
}

在上面的示例中,Student对象被写入到student.ser文件中。

如果要反序列化该对象,只需要使用ObjectInputStream读取文件并调用readObject()方法即可。

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.ser"))) {
    Student student = (Student) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}
序列化子类的父类

如果想要序列化一个基类对象,那么该基类也必须要实现Serializable接口。否则,在序列化子类时会抛出NotSerializableException异常。

class Person {
    ...
}

class Student extends Person implements Serializable {
    ...
}

在这个例子中,Person没有实现Serializable接口,因此尝试序列化Student时,会抛出NotSerializableException异常。

Student student = new Student("Alice", 20, "Computer Science");

try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.ser"))) {
    oos.writeObject(student);
} catch (IOException e) { 
    e.printStackTrace();
}

会得到如下异常:

java.io.NotSerializableException: Person
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    ...

为了解决这个问题,可以让Person也实现Serializable接口。

class Person implements Serializable {
    ...
}

class Student extends Person implements Serializable {
    ...
}

这样,就可以正常地序列化Student对象。

序列化多层继承关系

如果有多层继承关系,那么在序列化子类时,Java序列化机制会自动序列化父类。

class Person implements Serializable {
    ...
}

class Student extends Person implements Serializable {
    ...
}

class GraduateStudent extends Student implements Serializable {
    ...
}

在这个例子中,GraduateStudentStudent的子类,StudentPerson的子类。在序列化GraduateStudent对象时,StudentPerson的数据都会被序列化。因此,PersonStudent也必须实现Serializable接口。

GraduateStudent graduateStudent = new GraduateStudent("Bob", 25, "Computer Science", "Machine Learning");

try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("graduateStudent.ser"))) {
    oos.writeObject(graduateStudent);
} catch (IOException e) { 
    e.printStackTrace();
}
自定义序列化

如果不想序列化某个父类字段,可以使用transient关键字。

class Person implements Serializable {
    private String name;
    private transient int age;
    ...
}

class Student extends Person implements Serializable {
    private String major;
    ...
}

在这个例子中,Person类中的age字段被声明为transient,即不会被序列化。在序列化Student对象时,Person类中的name字段会被序列化,但age字段不会被序列化。

如果需要对序列化和反序列化过程进行更精细的控制,可以实现Serializable接口中的writeObject()readObject()方法。

class Student extends Person implements Serializable {
    private String major;
    ...

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        ...
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        ...
    }
}

在这个例子中,writeObject()方法会在序列化Student对象时被调用,readObject()方法会在反序列化Student对象时被调用。在这些方法中,可以自己编写序列化和反序列化的实现。

总结

在Java中,使用带有继承的类进行对象序列化时,需要将所有类声明为Serializable。在序列化多层继承关系时,所有父类都会被序列化。如果需要对序列化和反序列化过程进行更精细的控制,可以实现writeObject()readObject()方法。