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
是否一致。如果不一致,就会抛出异常: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 许可协议,转载请注明出处。