DRAGON BLOG
首页
搜索
自用站点
  •   share-chart
  •   Web-SSH
  •   Uptime-Kuma
往期整理
  •   历史归档
  •   文章分类
  •   文章标签
关于我
DRAGON
文章
6
分类
3
标签
6
首页
搜索
自用站点
share-chart
Web-SSH
Uptime-Kuma
往期整理
历史归档
文章分类
文章标签
关于我
技术分享
JAVA序列化与反序列化
发布于: 2024-10-2
最后更新: 2024-11-20
次查看
JAVA
type
status
date
slug
summary
tags
category
icon
password
comment
💁‍♂️
前言: Java 是一种广泛使用的编程语言,而“序列化”是 Java 中一个非常重要的概念。对于初学者来说,理解序列化的概念可能有些晦涩。所以本文将用最通俗易懂的语言,从“是什么”、“为什么”、“怎么样”三个角度,带你彻底搞懂 Java 的序列化。
 

📝 主旨内容

一、Java序列化是什么?

 
序列化(Serialization) 是指将 Java 对象转换为字节流的过程。反过来,把字节流重新转换为 Java 对象的过程叫做 反序列化(Deserialization)。
  • 字节流是什么? 字节流是一串可以存储在文件中、通过网络传输的二进制数据。
  • 序列化的本质是什么? 就是把一个 Java 对象“压缩”成一种可存储或传输的形式,反序列化再把它“还原”出来。

举个通俗的例子

假设你写了一封信(Java 对象),你需要把信寄给朋友(网络传输)。寄信的过程就相当于“序列化”,朋友收到信并阅读它的过程就是“反序列化”。
 

二、为什么需要序列化呢?

 

1. 持久化对象

有时候,我们需要把对象保存到磁盘中,以便在程序退出后下次还能继续使用这些数据。例如:
  • 保存用户的登录信息。
  • 记录游戏进度。 序列化可以把对象存储到文件中或数据库中,等需要时再反序列化取出来。

2. 网络传输对象

当你开发网络应用时,可能需要将一个对象从一个机器发送到另一个机器,比如:
  • 在分布式系统中,服务之间需要传递对象。
  • 客户端和服务器之间的通信。 通过序列化,Java 对象可以方便地通过网络传输。

3. 缓存

序列化也可以用来实现缓存。当数据被序列化存储后,未来可以快速加载,不用重复计算。
 

三、如何实现序列化?

 

1. 序列化的核心接口:Serializable

在 Java 中,序列化通过 java.io.Serializable 接口实现。一个类如果想被序列化,必须实现 Serializable 接口。

示例代码:

关键点:

  • Serializable 是一个标记接口,它不包含任何方法,只是用来告诉 Java 虚拟机这个类可以被序列化。
  • serialVersionUID 是一个唯一标识符,用来确保反序列化时类的版本兼容。如果没有声明,会由 JVM 自动生成,但强烈建议显式声明。
 

注意:

如果一个类不实现 Serializable 接口,试图对其对象进行序列化时会抛出 java.io.NotSerializableException 异常。
只有实现了 Serializable 接口的类,Java 才会允许你对它进行序列化。

1.1 什么是 serialVersionUID?为什么需要它?

作用:

  • serialVersionUID 是 Java 序列化机制用来验证序列化和反序列化的 版本一致性 的标识符。
  • 每个实现了 Serializable 接口的类都有一个隐式或显式的 serialVersionUID。如果你没有显式定义,Java 会根据类名、字段等自动生成一个 serialVersionUID。
 

举个例子:

假设你对某个类进行了序列化,然后稍后你修改了这个类的代码(比如增加了一个字段)。如果没有显式的 serialVersionUID,Java 在反序列化时会判断原始类和当前类的 serialVersionUID 是否一致。如果不一致,就会抛出异常:
这是因为两次生成的 serialVersionUID 不一致。

显式声明 serialVersionUID 的作用:

  • 当你显式声明了 serialVersionUID,即使你修改了类的结构,只要 serialVersionUID 不变,反序列化仍然可以成功。
 
注意:
  • 新增字段的值会为默认值(如 int 为 0,对象为 null)。
  • 删除字段可能导致反序列化后的对象缺少数据。
 
所以显式定义serialVersionUID的作用是不可言喻的!
 

2. 实现序列化和反序列化

(1)序列化

通过 ObjectOutputStream 将对象转换为字节流并保存到文件中。

(2)反序列化

通过 ObjectInputStream 将字节流还原为对象。
 

3. 注意事项

  • 字段的 transient 关键字 如果某个字段不想被序列化,可以用 transient 修饰。
    • 静态字段不会被序列化 序列化只保存对象的实例变量,而静态变量属于类,不会被序列化。
    • serialVersionUID 的重要性 如果类的定义发生了改变(比如添加新字段),反序列化可能会失败。这时 serialVersionUID 就可以保证版本的兼容性。
     

    四、深入理解 Java 序列化的机制

     

    1. 序列化的底层工作原理

    序列化时,JVM 将对象的:
    • 类信息(类名、版本号)
    • 实例数据(变量值)
    • 引用对象(嵌套对象) 一并写入到字节流中。
    反序列化时,JVM 通过 serialVersionUID 检查字节流中的类版本是否与当前类匹配,匹配才能成功恢复对象。

    2. 序列化的限制

    虽然序列化功能很强大,但也有以下限制:
    • 性能开销:序列化和反序列化需要时间,尤其是大对象。
    • 安全性问题:序列化的数据可能被恶意修改,导致反序列化后的对象行为异常。因此,在反序列化时要小心不可信数据。
    • 跨语言兼容性差:Java 的序列化机制是 Java 专属的,其他语言无法直接解析。

    3. 替代方案

    对于性能和安全性有更高要求的场景,可以考虑以下替代方案:
    • JSON 或 XML 格式:使用库(如 Jackson、Gson)将对象转换为 JSON,再传输或存储。
    • Protocol Buffers:Google 提供的一种高效序列化方案。
     

    🤗 总结归纳

     
    • implements Serializable:告诉 Java 这个类的对象可以序列化,用于持久化、网络传输等场景。
    • serialVersionUID:用于确保序列化和反序列化的版本一致性,避免因为类的修改导致反序列化失败。
    • 如果不用这些?如果类从不需要序列化(比如只是局部临时使用的普通类),可以不实现 Serializable,也无需关心 serialVersionUID。如果需要支持序列化,建议显式声明 serialVersionUID,以确保版本的可控性和兼容性。
    类型
    是否需要实现 Serializable
    理由
    实体类
    大多数情况下需要
    分布式系统或持久化框架可能需要支持序列化,建议实现 Serializable。
    VO
    通常需要
    VO 通常通过网络传输,或作为 API 的返回值,推荐实现 Serializable。
    DTO
    大多数情况下需要
    DTO 用于数据传输,特别是在分布式系统中,应实现 Serializable。
    局部类
    不需要
    如果类只在方法或模块内部使用,无需持久化或传输,不需要 Serializable。
     
     
    💡
    有关相关问题,欢迎您在底部评论区留言,一起交流~
    • 作者:DRAGON
    • 链接:https://dragonhub.me/article/JAVA-Serialization
    • 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
    Cherry Studio搭配DeepSeek + RAG知识库 NGINX的反向代理后的跨域问题
    Loading...
    目录
    0%
    📝 主旨内容一、Java序列化是什么?举个通俗的例子二、为什么需要序列化呢?1. 持久化对象2. 网络传输对象3. 缓存三、如何实现序列化?1. 序列化的核心接口:Serializable示例代码:关键点:注意:1.1 什么是 serialVersionUID?为什么需要它?作用:举个例子:显式声明 serialVersionUID 的作用:2. 实现序列化和反序列化(1)序列化(2)反序列化3. 注意事项四、深入理解 Java 序列化的机制1. 序列化的底层工作原理2. 序列化的限制3. 替代方案🤗 总结归纳
    DRAGON
    DRAGON
    一个普通的干饭人🍚
    文章
    6
    分类
    3
    标签
    6
    最新发布
    Cherry Studio搭配DeepSeek + RAG知识库
    Cherry Studio搭配DeepSeek + RAG知识库
    2025-2-7
    NGINX的反向代理后的跨域问题
    NGINX的反向代理后的跨域问题
    2024-11-21
    JAVA序列化与反序列化
    JAVA序列化与反序列化
    2024-11-20
    OpenWebUI中使用FLUX绘画(硅基流动)
    OpenWebUI中使用FLUX绘画(硅基流动)
    2024-11-16
    通过Saas回源给tunnel加速
    通过Saas回源给tunnel加速
    2024-11-16
    docker网桥配置完后,需要开启转发不然容器启动后,就会没有网络。
    docker网桥配置完后,需要开启转发不然容器启动后,就会没有网络。
    2024-11-16
    公告
    🎉这是个空荡荡的小窝🎉
     
     
    目录
    0%
    📝 主旨内容一、Java序列化是什么?举个通俗的例子二、为什么需要序列化呢?1. 持久化对象2. 网络传输对象3. 缓存三、如何实现序列化?1. 序列化的核心接口:Serializable示例代码:关键点:注意:1.1 什么是 serialVersionUID?为什么需要它?作用:举个例子:显式声明 serialVersionUID 的作用:2. 实现序列化和反序列化(1)序列化(2)反序列化3. 注意事项四、深入理解 Java 序列化的机制1. 序列化的底层工作原理2. 序列化的限制3. 替代方案🤗 总结归纳
    2023-2025DRAGON.

    DRAGON BLOG | 一个普通的干饭人🍚

    Powered byNotionNext 4.7.7.