Java的POJO类为什么要实现Serializable接口

简介: Java的POJO类为什么要实现Serializable接口

遇到过的问题

在分布式架构中,项目结构一般会把公用的部分抽取到一个单独的项目中,比如,与数据库映射的类 User 放到 xx-core 工程中,对 User 的操作(CRUD)封装成一个服务,如 xx-service,在这个 service 中引入 xx-core 依赖,然后对外提供接口服务能力。下面看一下 User 类的代码:


public class User {
    private String name;    private Integer age;
    // getter and setter    // ...
    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

问题一

xx-service 项目中进行单元测试,没有问题,一旦另一个项目通过网络(RPC或 HTTP)远程调用 xx-service 中的接口,就会报序列化异常,通过日志很快能发现 User 类没有实现 Serializable 接口,修正代码如下:


public class User implements Serializable {
    private String name;    private Integer age;
    // getter and setter    // ...
    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

再次调用一切正常。

问题二

随着需求的迭代,需要对 User 类进行字段扩展以满足新的需求,如对 User 增加手机号字段,代码如下:



public class User implements Serializable {
    private String name;    private Integer age;    // 新增字段    private String phone;
    // getter and setter    // ...
    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

新服务 xx-service 发布上线后,发布有部分依赖 xx-core 工程的报出


Exception in thread "main" java.io.InvalidClassException: com.xxx.User; local class incompatible: stream classdesc serialVersionUID = 1035612825366362028, local class serialVersionUID = -1830850955695931978

报错原因为序列化与反序列化产生的 serialVersionUID 不一致,接下来在上面 User 类的基础上显示指定一个 serialVersionUID 问题解决,利用开发工具自动生成。如果在使用过程中修改这个 serialVersionUID 的值也会报同样的异常,所以请不要随意修改这个字段。修改后的代码如下:



public class User implements Serializable {
    // 不用关心具体的数字,也不要随意修改    private static final long serialVersionUID = 1035612825366362028L;
    private String name;    private Integer age;    private String phone;
    // getter and setter    // ...
    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

在阿里 Java 开发手册也有这样的强制规范

【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败; 如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。

总结

什么是序列化和反序列化?

序列化:把对象转换为字节序列的过程称为对象的序列化。反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

什么时候需要用到序列化和反序列化呢?

当我们只在本地 JVM 里运行下 Java 实例,这个时候是不需要序列化和反序列化的,但当我们需要将内存中的对象持久化到磁盘, 数据库中时, 当我们需要与浏览器进行交互时, 当我们需要实现 RPC 时, 这个时候就需要序列化和反序列化了。

总之,只要我们对内存中的对象进行持久化或网络传输,这个时候都需要序列化和反序列化。

为什么还要显示指定 serialVersionUID 的值?

如果不显示指定 serialVersionUID,JVM 在序列化时会根据属性自动生成一个 serialVersionUID,然后与属性一起序列化,再进行持久化或网络传输。在反序列化时,JVM 会再根据属性自动生成一个新版serialVersionUID, 然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功,否则报错。

如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID,但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.

在实际开发中,我们的类会不断迭代,一旦类被修改了,那旧对象反序列化就会报错。所以在实际开发中,我们都会显示指定一个serialVersionUID

Java序列化的其他特性

transientstatic 关键字修饰的属性不会被序列化。

最佳实践

在定义 POJO 类时都实现 Serializable 接口,并且生成 serialVersionUID 属性,不管值是什么,都不要进行修改。

推荐阅读

Java的BigDecimal里方法应该这样用

干掉 try catch !

别再用if-else了,用注解去代替他吧

如果觉得还有帮助的话,你的关注和转发是对我最大的支持,O(∩_∩)O:

相关文章
|
6天前
|
存储 Java
Java Scanner类详解与应用
Java Scanner类详解与应用
|
1天前
|
存储 并行计算 Java
Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
8 2
|
1天前
|
安全 Java 测试技术
滚雪球学Java(51):掌握Java Date类:用法、弊端与时间戳转换技巧,助你轻松处理日期时间
【6月更文挑战第5天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
13 0
滚雪球学Java(51):掌握Java Date类:用法、弊端与时间戳转换技巧,助你轻松处理日期时间
|
3天前
|
Java 编译器
31. 【Java教程】枚举类
31. 【Java教程】枚举类
8 1
|
3天前
|
小程序 Java
28. 【Java教程】Scanner 类
28. 【Java教程】Scanner 类
18 7
|
3天前
|
Java 索引
26. 【Java教程】 String类
26. 【Java教程】 String类
15 0
|
3天前
|
安全 Java 数据安全/隐私保护
23. 【Java教程】接口
23. 【Java教程】接口
5 0
|
3天前
|
Java 编译器 C语言
18. 【Java教程】类和对象
18. 【Java教程】类和对象
7 1
|
3天前
|
Java API 应用服务中间件
Java一分钟之-JNDI:Java命名和目录接口
【6月更文挑战第3天】JNDI,Java Naming and Directory Interface,是Java的API,用于访问命名和目录服务。本文聚焦JNDI常见问题和易错点:1) 初始化Context时需正确配置环境属性;2) 查找资源时确保名称与服务器配置一致;3) 别忘了导入JNDI库和妥善处理异常;4) 使用后记得关闭资源。理解JNDI并避免这些问题能提升应用的可维护性和灵活性。
19 2
|
3天前
|
Java 安全 测试技术
滚雪球学Java(49):如何使用StringBuilder类在Java中高效地处理字符串?
【6月更文挑战第3天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
14 3
滚雪球学Java(49):如何使用StringBuilder类在Java中高效地处理字符串?