当前位置: 首页 > news >正文

孤舟笔记 基础篇十三 对象好好的为啥要“拆成零件“?序列化和反序列化到底在干嘛

文章目录

    • 一、先说结论:一个是拆零件,一个是拼回去
    • 二、为什么需要序列化?对象又不是快递
      • 1. 对象活在内存里,出不了门
      • 2. 序列化的本质:把对象变成"说明书"
    • 三、怎么让一个类支持序列化?
      • 1. 实现 Serializable 接口
      • 2. serialVersionUID:版本号不能忘
      • 3. transient:这个字段别打包
    • 四、序列化有哪些坑?
      • 1. 引用对象也必须可序列化
      • 2. static 字段不会被序列化
      • 3. 单例模式的天敌
    • 五、回到全貌:一图记住
    • 六、回答技巧与点评
      • 标准回答
      • 加分回答
      • 面试官点评

个人网站

你有没有想过一个问题:Java 对象活在内存里,程序一关就没了。那如果我想把一个对象存到文件里,或者通过网络发给另一台机器,怎么办?

内存里的对象是一坨复杂的数据结构,有引用、有指针、有嵌套——你没法直接把它"复制粘贴"到别的地方。必须先把它拆成一种通用格式(字节流),到了目的地再组装回去。这个"拆"和"装"的过程,就是序列化和反序列化。

一、先说结论:一个是拆零件,一个是拼回去

|| 维度 | 序列化(Serialization) | 反序列化(Deserialization) |
||------|------------------------|--------------------------|
|| 方向 | 对象 → 字节流 | 字节流 → 对象 |
|| 干了啥 | 把内存中的对象拆成可传输/可存储的格式 | 把字节流重新组装成内存中的对象 |
|| 生活比喻 | 把家具拆成零件打包搬家 | 到了新家把零件拼回家具 |
|| 核心方法 |ObjectOutputStream.writeObject()|ObjectInputStream.readObject()|
|| 用途 | 存储、传输、缓存 | 读取、接收、恢复 |

一句话记住:序列化是打包发货,反序列化是拆箱收货。

二、为什么需要序列化?对象又不是快递

1. 对象活在内存里,出不了门

Java 对象存在于 JVM 的堆内存中,它的数据包含内存地址引用。你直接把对象发给别人?对不起,人家 JVM 的内存地址跟你完全不同,引用全废。

就好比你搬家,你没法把一个衣柜整体搬走——它太大了,门都出不去。但你把它拆成板子、螺丝、把手,打包成箱子,就能运走了。到了新家再照着说明书拼回来。

2. 序列化的本质:把对象变成"说明书"

// 序列化:对象 → 字节流Useruser=newUser("张三",25,"zhangsan@qq.com");ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("user.dat"));oos.writeObject(user);// 把 user 对象"拆成零件"写入文件oos.close();// 反序列化:字节流 → 对象ObjectInputStreamois=newObjectInputStream(newFileInputStream("user.dat"));Userrestored=(User)ois.readObject();// 按"说明书"把对象拼回来ois.close();System.out.println(restored.getName());// 张三,原封不动

序列化时,JVM 把对象的所有字段值(包括嵌套对象)按顺序写入字节流。反序列化时,按照同样的顺序读回来,重新构建对象。就像宜家家具的说明书——只要按步骤来,到哪儿都能拼出一模一样的柜子。

三、怎么让一个类支持序列化?

1. 实现 Serializable 接口

publicclassUserimplementsSerializable{privateStringname;privateintage;privateStringemail;// 就这么简单,不需要实现任何方法}

Serializable是一个标记接口,里面啥方法都没有。它的作用就是告诉 JVM:"这个类的对象可以被拆成零件运输。"没打这个标记的,JVM 不让序列化,直接抛NotSerializableException

这就好比行李过安检——你的箱子贴了"可托运"标签,航空公司才让你托运;没标签的,对不起,不让上飞机。

2. serialVersionUID:版本号不能忘

publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=1L;// 👈 这个很重要!privateStringname;privateintage;}

serialVersionUID是序列化的版本号。反序列化时,JVM 会比对字节流中的版本号和当前类的版本号,不一致就抛InvalidClassException

想象你拿着去年的宜家说明书,但今年的柜子改了设计——螺丝孔位置变了,拼出来肯定不对。版本号就是用来防止这种"说明书和产品对不上"的情况。

如果你不写,JVM 会自动生成一个,但只要类有任何改动(加个字段、改个方法),自动生成的版本号就会变,之前序列化的数据就反序列化不回来了。所以一定要手动写,别偷懒。

3. transient:这个字段别打包

publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateStringname;privatetransientStringpassword;// 👈 序列化时跳过这个字段}

transient关键字告诉 JVM:"这个字段不用打包,搬的时候扔掉。"反序列化后,password会是默认值(null)。适用于密码、临时缓存等敏感或无意义的字段。

四、序列化有哪些坑?

1. 引用对象也必须可序列化

publicclassOrderimplementsSerializable{privateUseruser;// User 也必须实现 Serializable,否则报错privateList<Item>items;// ArrayList 已经实现了 Serializable ✅}

对象里嵌套的其他对象,也必须实现Serializable,否则整条链路都会炸。就像搬家时衣柜里还锁了个保险箱——你不把保险箱也拆了,衣柜也搬不走。

2. static 字段不会被序列化

static字段属于类,不属于对象,序列化只管对象的数据。所以反序列化后 static 字段的值是当前 JVM 中该类的值,不是序列化时的值。

3. 单例模式的天敌

反序列化时会创建新对象,绕过单例的私有构造器。解决方案是重写readResolve()方法,返回已有的单例实例。

五、回到全貌:一图记住

序列化 反序列化 ────────────────────────────────────────── 对象 → 字节流 字节流 → 对象 ObjectOutputStream.writeObject ObjectInputStream.readObject 打包发货 拆箱收货 关键点: ├── 实现 Serializable 接口(标记接口,无方法) ├── 手动写 serialVersionUID(版本号,防反序列化失败) ├── transient 跳过字段(敏感数据不打包) ├── 引用对象也要可序列化(整条链路都要能拆) └── static 不参与序列化(属于类,不属于对象)

口诀:序列化是打包,反序列化是拆箱;实现接口贴标签,版本号别忘写,transient 不打包。

六、回答技巧与点评

标准回答

序列化是将 Java 对象转换为字节流的过程,反序列化是将字节流恢复为 Java 对象的过程。序列化用于对象的持久化存储和网络传输,因为对象存在于内存中无法直接跨 JVM 传递。实现序列化需要实现 Serializable 接口,建议手动声明 serialVersionUID 防止版本不一致导致反序列化失败,用 transient 关键字可以跳过不需要序列化的字段。

加分回答

  1. 提到常见场景:RPC 框架(Dubbo)、分布式缓存(Redis 存对象)、消息队列(Kafka 传对象)都依赖序列化
  2. 提到替代方案:Java 原生序列化性能差且不安全,实际项目中常用 JSON(Jackson)、Protobuf、Hessian 等跨语言方案
  3. 提到安全风险:反序列化漏洞是 Java 安全重灾区,不要反序列化不可信的数据源,可以使用白名单过滤

面试官点评

这道题看似在考概念,其实考的是你对 Java 对象生命周期和跨进程通信的理解。如果只说"对象转字节流"就太浅了——能讲出 serialVersionUID 的作用、transient 的使用场景、引用链的序列化要求,才说明你真用过。如果能顺带提到原生序列化的缺点和替代方案,面试官会觉得你有工程实践经验,加分。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

http://www.jsqmd.com/news/695123/

相关文章:

  • PADS模块复用踩坑实录:为什么我的器件和走线一ECO就消失了?
  • X86服务器及“机架、塔式、刀片”三类服务器分类
  • 别再只会用空格了!这5个Google/Baidu搜索操作符,帮你精准找到任何资料(附实战案例)
  • 【VSCode多智能体调试终极指南】:20年IDE专家亲授5大实战技巧,90%开发者还不知道的调试黑科技
  • Stata实操:用双重差分法(DID)评估政策效果,从数据清洗到结果解读保姆级教程
  • 2026 SERP + LLM 训练数据采集指南(Bright Data MCP + Dify)
  • 2026年4月襄阳社区广告投放指南:为何襄阳上善传媒是本地商家的优选伙伴? - 2026年企业推荐榜
  • CLIP双塔架构拆解:从ResNet与ViT的视觉编码到文本Transformer的协同
  • 北景云光伏监控运维系统 让光伏电站“看得见、管得住、用得好
  • SubAgent 原理深度解析:AI 系统如何通过委托实现专业化分工
  • 5大核心功能揭秘:Happy Island Designer如何帮你打造完美岛屿规划
  • 反射即性能?不!C++26元编程性能断崖预警,92%开发者忽略的constexpr反射副作用,立即修复清单
  • HC7702高效PFM同步升压DC-DC转换芯片
  • 什么牌子的运动耳机适合健身戴?适合健身戴的运动耳机合集来了
  • DBeaver SQL格式化踩坑实录:手把手教你配置sql-formatter第三方插件(Windows环境)
  • 告别地面误检!Patchwork算法在ROS2与Autoware.Universe中的实战调优指南
  • 别再只会用官网例子了!Vxe-Table过滤功能深度自定义:从下拉框到服务端筛选的完整配置流程
  • 2026AI营销解决方案技术架构拆解与落地指南:人工智能营销企业、人工智能营销商业化、AI应用上市公司、AI应用企业选择指南 - 优质品牌商家
  • Python自动化AutoCAD:突破性技术如何重塑工程设计工作流
  • 打破数字枷锁:现代音乐解锁工具的技术革命与应用实践
  • SK时科Shikues原厂原装一级代理分销经销
  • Zotero-SciHub插件:3分钟搞定学术文献PDF自动下载,效率提升10倍
  • Win11环境下海康摄像头ONVIF协议设备发现与集成实战
  • 回归最经典的“CNN+Mamba+UNet”组合套路,发文稳准狠!
  • 国产M0核风机量产程序开发方案:基于国产M0核MCU平台的FOC电机控制开发方案
  • CloudCompare CANUPO分类器训练避坑实录:我的‘地面’和‘非地面’是怎么分清楚的?
  • Docker-compose 编排Samba:打造跨平台文件共享中心
  • Hermes Agent 爆火了:腾讯云/本地一键部署,微信接入后终于有了“会自我进化”的 AI 助手
  • 常见细胞因子检测方法全解析
  • AI Agent 爆发前夜:从大模型到智能体的技术演进与商业落地