招行2面:为什么需要序列化和反序列?为什么不能直接使用对象?
Hi,你好,我是猿java。
工作中,我们经常听到序列化
和反序列化
,那么,什么是序列化
?什么又是反序列化
?这篇文章,我们来分析一个招商的面试题:为什么需要序列化
和反序列化
?
1. 什么是序列化和反序列化?
简单来说,序列化
就是把一个Java对象转换成一系列字节的过程,这些字节可以被存储到文件、数据库,或者通过网络传输。反过来,反序列化
则是把这些字节重新转换成Java对象的过程。
想象一下,你有一个手机应用中的用户对象(比如用户的名字、年龄等信息)。如果你想将这个用户对象存储起来,或者发送给服务器,你就需要先序列化它。等到需要使用的时候,再通过反序列化把它恢复成原来的对象。
2. 为什么需要序列化?
“为什么需要序列化?为什么不能直接使用对象呢?”这确实是一个好问题,而且很多工作多年的程序员不一定能回答清楚。综合来看:需要序列化的主要原因有以下三点:
- 持久化存储:当你需要将对象的数据保存到磁盘或数据库中时,必须把对象转换成一系列字节。
- 网络传输:在分布式系统中,不同的机器需要交换对象数据,序列化是实现这一点的关键。
- 深拷贝:有时候需要创建对象的副本,序列化和反序列化可以帮助你实现深拷贝。
更直白的说,序列化是为了实现持久化和网络传输,对象是应用层的东西,不同的语言(比如:java,go,python)创建的对象还不一样,实现持久化和网络传输的载体不认这些对象。
3. 序列化的原理分析
Java中的序列化是通过实现java.io.Serializable
接口来实现的。这个接口是一个标记接口,意味着它本身没有任何方法,只是用来标记这个类的对象是可序列化的。
当你序列化一个对象时,Java会将对象的所有非瞬态(transient
)和非静态字段的值转换成字节流。这包括对象的基本数据类型、引用类型,甚至是继承自父类的字段。
序列化的步骤
- 实现
Serializable
接口:你的类需要实现这个接口。 - **创建
ObjectOutputStream
**:用于将对象转换成字节流。 - 调用
writeObject
方法:将对象写入输出流。 - 关闭流:别忘了关闭流以释放资源。
反序列化的步骤大致相同,只不过是使用ObjectInputStream
和readObject
方法。
4. 示例演示
让我们通过一个简单的例子来看看实际操作是怎样的。
定义一个可序列化的类
1 | import java.io.Serializable; |
序列化对象
1 | import java.io.FileOutputStream; |
运行上述代码后,你会发现当前目录下生成了一个名为user.ser
的文件,这就是序列化后的字节流。
反序列化对象
1 | import java.io.FileInputStream; |
运行这段代码,你会看到输出:
1 | 反序列化后的对象: User{name='Alice', age=30, password='null'} |
注意到password
字段为空,这是因为它被声明为transient
,在序列化过程中被忽略了。
5. 常见问题与注意事项
serialVersionUID是干嘛的?
serialVersionUID
是序列化时用来验证版本兼容性的一个标识符。如果你不显式定义它,Java会根据类的结构自动生成。但为了避免类结构变化导致序列化失败,建议手动定义一个固定的值。
继承关系中的序列化
如果一个类的父类没有实现Serializable
接口,那么在序列化子类对象时,父类的字段不会被序列化。反序列化时,父类的构造函数会被调用初始化父类部分。
处理敏感信息
使用transient
关键字可以防止敏感信息被序列化,比如密码字段。此外,你也可以自定义序列化逻辑,通过实现writeObject
和readObject
方法来更精细地控制序列化过程。
6. 总结
本文,我们深入浅出地探讨了Java中的序列化和反序列化,从基本概念到原理分析,再到实际的代码示例,希望你对这两个重要的技术点有了更清晰的理解。
为什么需要序列化和反序列化?
最直白的说,如果不进行持久化和网络传输,根本不需要序列化和反序列化。如果需要实现持久化和网络传输,就必须序列化和反序列化,因为对象是应用层的东西,不同的语言(比如:java,go,python)创建的对象还不一样,实现持久化和网络传输的载体根本不认这些对象。
7. 交流学习
最后,把猿哥的座右铭送给你:投资自己才是最大的财富。 如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。
