为什么需要序列化和反序列化?为什么不能直接使用对象?

做 Java 后端开发,序列化和反序列化是绕不开的基础知识点——不管是 RPC 调用、数据持久化,还是 Redis 缓存、消息队列,几乎处处都能见到它的身影。
但很多新手(甚至工作 1-2 年的开发者)都有一个灵魂拷问:
为什么不能直接使用对象?非要多此一举做序列化和反序列化?
一、先搞懂:什么是序列化和反序列化?(大白话版)
先抛开专业术语,用最通俗的话解释:
- 序列化:把内存中的「Java 对象」,转换成「可传输、可存储的二进制流/字符串」(比如 JSON、字节数组)的过程。
类比:把你房间里的家具(对象),拆解成一个个可打包、可运输的零件(二进制流)。 - 反序列化:把传输/存储的「二进制流/字符串」,重新转换成内存中「可使用的 Java 对象」的过程。
类比:把运输过来的零件,重新组装成原来的家具(对象),供你使用。
举个最常见的例子:
你用 SpringBoot 写接口,返回一个 User 对象给前端,底层其实就是把 User 对象序列化成 JSON 字符串,前端拿到 JSON 后,再反序列化成自己能识别的对象,才能展示数据。
// 序列化(Java 对象 → JSON 字符串) User user = new User(1L, "张三", 25); String json = JSON.toJSONString(user); // 序列化操作 // 反序列化(JSON 字符串 → Java 对象) User user2 = JSON.parseObject(json, User.class); // 反序列化操作
这就是最基础的序列化场景,你每天都在写,只是可能没意识到。
二、核心问题:为什么不能直接使用对象?
这是新手最困惑的点——我在代码里直接 new 一个对象,调用它的方法、获取它的属性,用得好好的,为啥非要转换成二进制流/JSON?
答案很简单:对象是「内存中的临时数据」,无法跨场景传递和持久化。
我们分 4 个最常见的业务场景,逐一说明,你就懂了:
场景 1:跨网络传输(最核心场景)
比如微服务之间的 RPC 调用(Dubbo、Feign),或者前后端接口交互——你的服务在服务器 A 的内存里,另一个服务/前端在服务器 B/用户浏览器里,它们不在同一个内存空间。
对象是「存在于特定内存中的数据结构」,只能在当前 JVM 进程中使用,无法直接“飘”到另一个 JVM/浏览器里。
就像你在自己家里的家具(对象),不能直接搬到邻居家(另一个内存空间),必须拆解成零件(序列化),运输过去后再组装(反序列化)。
如果直接传递对象,会报什么错?
你可以试试用 Socket 直接传递一个 User 对象,会抛出NotSerializableException(未序列化异常)——JVM 明确告诉你:对象不能直接传输,必须先序列化。
场景 2:数据持久化(存到磁盘/数据库)
我们经常需要把对象的状态保存起来,比如存到文件、Redis、数据库中,供后续使用。
但磁盘、Redis、数据库,只能存储「二进制、字符串、数字」这类基础数据,无法直接存储内存中的“对象”。
比如你想把 User 对象存到 Redis 里,直接存redisTemplate.opsForValue().set("user", user)会报错——Redis 不认识 Java 对象,只能存序列化后的 JSON 字符串或字节数组。
只有把对象序列化后,转换成 Redis 能识别的格式,才能存进去;后续读取时,再反序列化回 User 对象,才能正常使用。
场景 3:跨语言交互
你的 Java 服务,可能需要和 Python、Go、前端 JS 服务交互——不同语言的“对象”格式完全不一样,Java 的 User 对象,Python 根本不认识。
这时候,就需要一个「通用的中间格式」(比如 JSON、Protobuf),把 Java 对象序列化成通用格式,其他语言拿到后,再反序列化成自己语言能识别的对象。
比如 Java 序列化 User 对象成 JSON,Python 拿到 JSON 后,反序列化成 Python 的字典/类实例,这样就能实现跨语言数据交互。如果直接传递 Java 对象,Python 只会收到一堆乱码,根本无法解析。
场景 4:对象深拷贝(避免引用传递坑)
日常开发中,我们经常需要复制一个对象(深拷贝),避免修改副本时影响原对象。
如果直接用赋值(User user2 = user1),只是传递引用,两个变量指向同一个内存地址,修改 user2,user1 也会跟着变。
而序列化+反序列化,是实现深拷贝的最简单方式之一:
把对象序列化后,再反序列化成一个新对象,这个新对象和原对象完全独立,没有引用关联,修改互不影响。
// 序列化+反序列化实现深拷贝
User user1 = new User(1L, "张三", 25);
String json = JSON.toJSONString(user1);
User user2 = JSON.parseObject(json, User.class);
user2.setName("李四");
System.out.println(user1.getName()); // 输出:张三(不受影响)
如果不序列化,直接用对象赋值,修改 user2 的名字,user1 的名字也会变成“李四”,这就是引用传递的坑——而序列化能完美解决这个问题。
三、补充:序列化的核心价值(总结)
看完上面 4 个场景,你应该能明白:序列化的本质,是打破“对象只能存在于当前内存”的限制,实现对象的跨场景传递和持久化。
它的核心价值有 3 点:
- 解决「跨内存/跨网络」的对象传递问题,让不同空间的服务能交互数据;
- 解决「对象持久化」问题,让对象能存到磁盘、缓存中,长期保存;
- 解决「跨语言交互」和「深拷贝」问题,简化开发,避免踩坑。
而直接使用对象,只能局限在当前 JVM 的内存中,无法跨场景使用——这就是为什么,我们不能直接用对象,必须做序列化和反序列化。
四、新手常踩的 2 个误区(避坑指南)
误区 1:所有对象都需要序列化?
不是!只有需要「跨场景传递、持久化」的对象,才需要序列化。
比如:
- 只是在当前方法里 new 一个对象,用完就销毁,不需要序列化;
- 作为 RPC 接口的参数/返回值、需要存 Redis/数据库的对象,必须序列化。
Java 中,实现序列化很简单,只要让类实现 Serializable 接口(标记接口,不需要重写任何方法)即可:
// 实现 Serializable 接口,标记该类可序列化
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
// getter/setter/构造方法...
}
误区 2:序列化越多越好?
不是!序列化和反序列化会消耗 CPU 和内存,频繁序列化会影响接口性能。
比如:
- 不需要跨场景的对象,不要序列化;
- 序列化时,只序列化必要的字段(用 transient 关键字排除不需要序列化的字段),减少数据体积。
public class User implements Serializable {
private Long id;
private String name;
private transient String password; // 密码不需要序列化,避免泄露
// getter/setter...
}
五、总结
一句话讲透核心:
对象是「内存中的临时数据」,只能在当前 JVM 中使用;序列化把对象变成「可传输、可存储的通用格式」,反序列化再把通用格式变回对象——本质是“对象的转换与复用”,解决跨场景使用的问题。
没有序列化,就没有微服务、没有前后端交互、没有缓存持久化——它是 Java 后端的“隐形基础设施”,看似简单,却贯穿整个开发流程。
下次再写 RPC 接口、存 Redis 的时候,就不会再疑惑“为什么要序列化”了——因为没有它,你的对象根本“传不出去、存不下来”。
以上关于为什么需要序列化和反序列化?为什么不能直接使用对象?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 为什么需要序列化和反序列化?为什么不能直接使用对象?
微信
支付宝