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:

相关文章
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
13天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
Java
在Java中实现接口的具体代码示例
可以根据具体的需求,创建更多的类来实现这个接口,以满足不同形状的计算需求。希望这个示例对你理解在 Java 中如何实现接口有所帮助。
92 38
|
16天前
|
数据采集 JSON Java
利用Java获取京东SKU接口指南
本文介绍如何使用Java通过京东API获取商品SKU信息。首先,需注册京东开放平台账号并创建应用以获取AppKey和AppSecret。接着,查阅API文档了解调用方法。明确商品ID后,构建请求参数并通过HTTP客户端发送请求。最后,解析返回的JSON数据提取SKU信息。注意遵守API调用频率限制及数据保护法规。此方法适用于电商平台及其他数据获取场景。
|
21天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
66 8
|
2月前
|
Java API
Java中内置的函数式接口
Java中内置的函数式接口
28 2
|
2月前
|
Java
在Java中如何实现接口?
实现接口是 Java 编程中的一个重要环节,它有助于提高代码的规范性、可扩展性和复用性。通过正确地实现接口,可以使代码更加灵活、易于维护和扩展。
171 64
|
2月前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
130 1
|
5月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
65 7