Java序列化为什么需要实现Serializable接口

1.为什么实现Serializable接口

话不多说,直接上Java序列化代码

 public void testSerialize() throws Exception {
        StudentAssess studentAssess=new StudentAssess();
        studentAssess.setStudentName("lan");

        try(ObjectOutputStream objectOutputStream =
                new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) )){
            objectOutputStream.writeObject( studentAssess );

        }

    }
复制代码

上面的代码就是序列化一个StudentAssess类的方式,我们查看writeObject的源码,发现里面writeObject0方法有段逻辑

 // remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
复制代码

可以得知,在序列化方法里面判断了,如果该对象不是字符串、数组、枚举,而且没有实现Serializable接口的话,在序列化的时候就会抛出NotSerializableException的异常

2.延申

1.什么属性不会被序列化

1、凡是被static修饰的字段是不会被序列化的
2、凡是被transient修饰符修饰的字段也是不会被序列化的

对于第一点,因为序列化保存的是对象的状态而非类的状态,所以会忽略static静态域也是理所应当的。

对于第二点,就需要了解一下transient修饰符的作用了。

如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如这个字段存放的是隐私值,如:密码等),那这时就可以用transient修饰符来修饰该字段。

2.serialVersionUID号有何用?

1、serialVersionUID是序列化前后的唯一标识符,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程
2、默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!,为了serialVersionUID的确定性,写代码时还是建议

3.readObject()函数有什么用

场景1:序列化和反序列化的过程其实是有漏洞的,因为从序列化到反序列化是有中间过程的,如果被别人拿到了中间字节流,然后加以伪造或者篡改,那反序列化出来的对象就会有一定风险了
场景2:反序列化后的单例对象和原单例对象并不相等

场景1解决方案:在readObject方法上加上逻辑判断,如果不符合就抛出异常


    // 调用默认的反序列化函数
    objectInputStream.defaultReadObject();

    // 手工检查反序列化后学生成绩的有效性,若发现有问题,即终止操作!
    if( 0 > score || 100 < score ) {
        throw new IllegalArgumentException("学生分数只能在0到100之间!");
    }
}
复制代码

场景2解决方案:在单例类中手写readResolve()函数,直接返回单例对象,来规避之

private Object readResolve() {
    return SingletonHolder.singleton;
}
复制代码

这样一来,当反序列化从流中读取对象时,readResolve()会被调用,用其中返回的对象替代反序列化新建的对象

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享