• Java序列化和反序列化


    在我们实际使用中,我们经常会遇到对象需要序列化的问题。我将通过下面几个问题,理解序列化的相关问题

    1. 什么是序列化和反序列化
    2. 什么时候需要进行序列化和反序列化
    3. 实现序列化和反序列化为什么要实现Serializable接口
    4. 在实现Serializable接口时,为什么要显示指定serialVersionUID的值
    5. Java的序列化的特性
    6. static为什么不需要被序列化

    什么是序列化和反序列化

    • 序列化:把Java内存对象转换为字节序列的过程称为对象的序列化
    • 反序列化:把字节序列恢复为Java内存对象的过程称为对象的反序列化

    什么时候需要序列化和反序列化

    当我们需要把Java的内存对象持久化到文件/数据库,或者进行网络传输时,就需要对对象进行序列化。

    实现序列化和反序列化为什么要实现Serializable接口

    在Java中,如果一个类实现了Serializable接口,JVM底层就会帮我们实现序列化和反序列化。如果我们不实现Serializable接口,那么就需要我们自己实现序列化和反序列化

    实现Serializable接口时,为什么需要指定serialVersionUID的值

    在Java中,如果一个实现了Serializable接口的类没有指定serialVersionUID,那么JVM在序列化时,会根据属性自动生成一个serialVersionUID,然后和属性一起序列化,再进行网络传输或者持久化。在反序列化时,JVM会再根据属性生成一个新版本的serialVersionUID,然后再用这个新版本和serialVersionUID和序列化时生成的旧版本的serialVersionUID进行计较。如果二者一样就可以序列化成功。反之,报错。

    如果我们显示指定了serialVersionUID,JVM在序列化和反序列化时,就会使用我们指定的serialVersionUID。这样我们就可以确保在反序列化时,serialVersionUID和之前的相同。

    在实际的开发中,不指定serialVersionUID的值。在我们修改一个类的属性,或者使用不同版本的jdk时,都有可能会导致自动生成serialVersionUID发生改变。从而会导致这个类在进行反序列化时失败。

    下面我们写一个例子:

    1. 测试实体类,Stu,实现了Serializable接口,但是不指定serialVersionUID
    1. public class Stu implements Serializable {
    2. private Integer id;
    3. private String name;
    4. public Integer getId() {
    5. return id;
    6. }
    7. public void setId(Integer id) {
    8. this.id = id;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. @Override
    17. public String toString() {
    18. return "Stu{" +
    19. "id=" + id +
    20. ", name='" + name + '\'' +
    21. '}';
    22. }
    23. }
    1. 测试类
    1. public class SerializableTest {
    2. private static void serialize(Stu stu) throws Exception {
    3. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("./1.txt")));
    4. oos.writeObject(stu);
    5. oos.close();
    6. }
    7. private static Stu deserialize() throws Exception {
    8. ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./1.txt")));
    9. return (Stu)ois.readObject();
    10. }
    11. @Test
    12. public void testSerializer() throws Exception {
    13. Stu stu = new Stu();
    14. stu.setId(18);
    15. stu.setName("hardy");
    16. System.out.println("before serializer" + stu);
    17. serialize(stu);
    18. Stu deserializeStu = deserialize();
    19. System.out.println("after deserializer:" + deserializeStu);
    20. }
    21. }

    此时,我们注释代码中序列化的的部分,给Stu再增加一个属性,再进行反序列化时就会报错:

    1. java.io.InvalidClassException: com.hardy.pojo.Stu; local class incompatible: stream classdesc serialVersionUID = -5638707890362050979, local class serialVersionUID = 6871763750857709435
    2. at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:715)
    3. at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2021)
    4. at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1890)
    5. at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2183)
    6. at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1707)
    7. at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:517)
    8. at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:475)
    9. at com.hardy.SerializableTest.deserialize(SerializableTest.java:23)
    10. at com.hardy.SerializableTest.testSerializer(SerializableTest.java:34)
    11. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    12. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    13. at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    14. at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    15. at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    16. at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    17. at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    18. at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    19. at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    20. at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    21. at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    22. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    23. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    24. at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    25. at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    26. at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    27. at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    28. at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    29. at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    30. at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    31. at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    32. at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    33. at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    34. at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    35. at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    36. at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    37. at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

    这是因为,序列化和反序列化的serialVersionUID不一致。我们在序列化时,增加serverVersionUID,就可以避免这个问题。

    Java序列化的特性

    1. 被transient关键字修饰的属性不会被序列化
    2. static属性也不会被序列化

    static属性为什么不会被实例化

    因为序列化序列的时内存对象,static属性先于对象加载。随着类的加载,static就加载好了。所以不会被序列化。

    serialVersionUID被static修饰,为什么会被序列化?
    serialVersionUID属性并没有被序列化,JVM在序列化时会自动给对象生成一个serialVersionUID。我们手动指定的话,serialVersionUID属性的话,这个值就会赋给serialVersionUID

  • 相关阅读:
    一次对requirements环境的配置
    LeetCode Cookbook 链表习题 上篇
    Oracle数据库中的table@xyz是什么意思?
    菜鸟先飞之初识Hive、安装教程及常见问题
    Prompt learning 教学[进阶篇]:简介Prompt框架并给出自然语言处理技术:Few-Shot Prompting、Self-Consistency等;项目实战搭建知识库内容机器人
    virtualbox centos 使用NAT模式上网
    【考研高数 武忠祥+880版 自用】高数第三章基础阶段思维导图
    自然语言处理 Paddle NLP - 结构化数据问答-理论
    [JavaScript]_[初级]_[关于forof或者for...of循环语句的用法]
    【AI大模型】Transformers大模型库(十一):Trainer训练类
  • 原文地址:https://blog.csdn.net/guanshengg/article/details/126558062