serialVersionUID可以避免在反序列时的失败

有什么用?

当我们将对象序列化后,对原来的对象进行了修改,比如新增了一个demo字段。
这是时候在将对象反序列化回来的时候,就会报错:

Exception in thread "main" java.io.InvalidClassException: com.github.codedrinker.p1413.User; local class incompatible: stream classdesc serialVersionUID = 6360520658036414457, local class serialVersionUID = -3025746955499933156

显示 serialVersionUID 不相同,反序列化失败了,可是我们没有定义serialVersionUID啊?
我们查看 java.io.ObjectStreamClass#writeNonProxy 源码,如果当前类(User)没有定义 serialVersionUID,就会调用java.io.ObjectStreamClass#computeDefaultSUID生成默认的序列化唯一标示。我们简单的看代码,发现他的生成规则是根据类名,结果明,方法和属性等参数生成的 hash 值,所以我们给 User 添加了 desc 属性,所以对应的 serialVersionUID 肯定会变化。

具体引用场景

数据库实体类的序列化:

  • 当你的数据库实体类需要序列化以在分布式系统中传输或缓存时,如果你预计数据库表结构可能发生变化,可以显式指定 serialVersionUID 以确保版本一致性。
    public class User implements Serializable {
    private static final long serialVersionUID = 123456789L;
    // ...
    }

    缓存对象的序列化:

  • 如果你将对象缓存在分布式缓存中,并且希望在应用升级后仍然能够反序列化旧版本的对象,可以使用 serialVersionUID
    public class CachedData implements Serializable {
    private static final long serialVersionUID = 987654321L;
    // ...
    }

    消息传递:

  • 在使用消息队列或其他方式进行异步消息传递时,如果你的消息类可能会在不同的应用版本之间传递,显式指定 serialVersionUID 是个好习惯。
    public class Message implements Serializable {
    private static final long serialVersionUID = 42L;
    // ...
    }

    分布式系统中的远程调用:

  • 当你在分布式系统中使用远程调用(例如 RMI)时,确保服务端和客户端对序列化的对象具有相同的 serialVersionUID 是非常重要的。
    
    public class RemoteObject implements Serializable {
    private static final long serialVersionUID = 567890123L;
    // ...
    }

## 测试
```java 
//实现序列化接口,但不定义serialVersionUID 字段
public class Demo implements Serializable {  

    public static void main(String[] args) {  
        Demo demo =new Demo();  
    }  
}

image.png
使用serialver 工具。

//实现序列化接口,自己定义serialVersionUID 字段
public class Demo implements Serializable {  

    private final static long serialVersionUID = 42L;  

    public static void main(String[] args) {  
        Demo demo =new Demo();  
    }  
}

image.png