MST

星途 面试题库

面试题:Java序列化:对象持久化的深入理解

在Java序列化实现对象持久化时,若对象引用了其他未实现Serializable接口的对象,会发生什么?如何解决这个问题?并且说明transient关键字在这种场景下的作用及原理。
19.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

问题一:若对象引用了其他未实现Serializable接口的对象会发生什么

当一个可序列化的Java对象引用了其他未实现Serializable接口的对象时,在进行序列化操作时,会抛出NotSerializableException异常。这是因为Java序列化机制要求所有被序列化对象所引用的对象也必须是可序列化的,以确保整个对象图都能够被正确地持久化和恢复。

问题二:如何解决这个问题

  1. 让被引用对象实现Serializable接口
    • 这是最直接的解决方法。如果被引用的类是自己编写的,直接让它实现Serializable接口即可。例如:
    class NonSerializableClass {
        // 类的成员和方法
    }
    // 修改为
    class SerializableClass implements Serializable {
        // 类的成员和方法
    }
    
  2. 使用transient关键字
    • 如果无法修改被引用类使其实现Serializable接口,可以将引用该类的字段声明为transient。这样在序列化时,该字段将不会被序列化,从而避免NotSerializableException。但在反序列化后,该字段的值将为其类型的默认值(对于对象类型为null,对于基本类型如int0等)。
    • 例如:
    import java.io.Serializable;
    
    class OuterClass implements Serializable {
        private transient NonSerializableClass nonSerializableObj;
        // 其他成员和方法
    }
    
  3. 自定义序列化和反序列化
    • 可以在包含引用的类中实现writeObjectreadObject方法来自定义序列化和反序列化过程。在writeObject方法中,可以选择不序列化不可序列化的对象,或者以某种可恢复的方式(如存储必要的状态信息)来处理它。在readObject方法中,根据存储的信息重新构建不可序列化的对象。
    • 示例代码如下:
    import java.io.*;
    
    class NonSerializableClass {
        private int value;
        public NonSerializableClass(int value) {
            this.value = value;
        }
        public int getValue() {
            return value;
        }
    }
    
    class OuterClass implements Serializable {
        private NonSerializableClass nonSerializableObj;
    
        public OuterClass(NonSerializableClass nonSerializableObj) {
            this.nonSerializableObj = nonSerializableObj;
        }
    
        private void writeObject(ObjectOutputStream out) throws IOException {
            // 可以只序列化必要的信息,比如NonSerializableClass中的value
            out.writeInt(nonSerializableObj.getValue());
        }
    
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            int value = in.readInt();
            nonSerializableObj = new NonSerializableClass(value);
        }
    }
    

问题三:transient关键字在这种场景下的作用及原理

  1. 作用
    • 在这种场景下,transient关键字用于标记那些不希望被序列化的字段。当对象被序列化时,被标记为transient的字段不会被写入到序列化流中。在反序列化时,这些字段会被初始化为其类型的默认值。这就避免了因为引用了不可序列化对象而导致的NotSerializableException异常。
  2. 原理
    • Java的序列化机制在处理对象的序列化和反序列化时,会检查对象的每个字段。对于标记为transient的字段,序列化机制会跳过对其的处理。在反序列化时,该字段直接按照其类型的默认初始化规则进行初始化。例如,对于对象类型的transient字段,反序列化后其值为null;对于int类型的transient字段,反序列化后其值为0。这是Java序列化机制在设计上对这种情况的一种处理方式,以保证序列化过程在遇到不可序列化字段时能够继续进行而不抛出异常。