Serializable 接口–java.io.serializable

理解Java对象序列化原理
本文深入探讨了Java中实现序列化功能的Serializable接口及其应用,包括如何使用ObjectOutputStream和ObjectInputStream进行对象的序列化与反序列化,以及包装流在这一过程中的作用。
  • 作用:类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
  • 实现了此接口的类有:String, Integer, Boolean, Long, ArrayList, List, File 等等。
  • 当java内存有一个对象的时候,我们想把它持久化保存起来,以后又可以从保存的数据中恢复成对象,这种情况经常在java中出现;sun公司为了方便实现这种功能,为用户提供了java.io.Serializable这个接口,这个接口中没有方法(又称为mini接口);
  • 在java编译器编译的时候对这种mini接口的对象进行特殊的编译,如果你的对象实现了serializable接口,那么就可以通过另一个工具ObjectOutputStream的方法writeObject(obj)来存储对象,而不需要具体知道存储该对象的具体细节;
  • 当恢复对象的时候,便可以通过ObjectInputStream.readObject(obj)来恢复对象;
  • 由于对于文件的存储目标是未知的,可以是硬盘也可以是网络上的某个主机,故ObjectInputStream和ObjectOutputStream并不是直接操作硬盘,也不是直接操作网络,而是操作可以直接访问物理设备的流的对象,如FileInputStream和FileOutputStream,即ObjectInputStream和ObjectOutputStream可以将直接进行底层操作的流FileInputStream和FileOutputStream包装起来,形成包装类,或者叫包装流;
  • 在形成包装类之后,对于需要保存的对象,先由包装类的外层将该对象转换为二进制字节,再将这些字节传递给内层可以直接进行物理设备操作的流如FileOutputStream,通过底层流保存到物理设备;
  • 包装的思想:首先,有个物理设备,如硬盘或网络,在内存中会有个流,如FileOutputStream,这个流只能有个write(byte[])方法,该方法写的是字节。而现在要将一个对象写入物理设备,故需要一个类来进行对象到字节的转换,这个转换类就是ObjectInputStream和ObjectOutputStream,通过这两个类转换,将转换后的字节数据交给FileOutputStream来进行物理设备操作。简单地说,就是一个分层的思想,也是“各尽其责”的思想。对象保存到物理设备,这个过程由两层完成,上层只负责将对象转换成字节数据,下层只负责将接收到的字节写入物理设备。
  • 所以包装流(如ObjectOutputStream)并不真正连接到底层物理设备,它起到一个中转的作用,因此它的构造方法一定要传递给它一个与底层设备相关的流,前者是把数据转换完了交给后者去读写物理设备;对于物理设备是什么设备,包装流并不知道,只有传递给它的底层设备相关的流(如FileOutputStream)知道。
### Java `Serializable` 接口的作用 `Serializable` 是一个标记接口,用于指示某个类的对象可以通过标准机制进行序列化操作[^1]。当一个类实现了 `java.io.Serializable` 接口时,意味着它的实例可以被转换成字节流形式以便于在网络上输或存储到文件中。 #### 主要作用 - **对象持久化**:允许将对象的状态保存至磁盘或其他外部介质上,并能够在稍后的时刻恢复其状态[^3]。 - **跨进程通信**:支持分布式环境中不同 JVM 之间递复杂数据结构(如 RMI 或者消息队列中的消息体),前提是这些数据能够被序列化并反序列化回来[^4]。 --- ### 使用场景分析 以下是几个典型的使用案例: 1. **远程过程调用 (RPC)** 当应用程序需要通过网络发送对象作为参数或者返回时,通常要求这些对象可序列化。例如,在微服务架构下客户端和服务端交互过程中经常涉及此类需求。 2. **缓存系统** 如果希望把某些临时性的计算结果暂存在内存以外的地方,则可能需要用到序列化技术来完成这一目标。 3. **数据库存储** 对象可以直接写入二进制格式的数据表字段里再读取出来重建原样副本[^5]. --- ### 如何实现 Serializable 接口? 由于它只是一个标志性质的空接口,因此只需要简单声明即可: ```java import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 1L; // 可选但推荐设置 String name; transient int age; // 不会被序列化的成员变量前加上transient关键字修饰 public Person(String n, int a){ this.name=n; this.age=a; } } ``` 上述代码片段展示了如何创建一个基本的支持序列化的类。注意这里还包含了两个重要概念: - `serialVersionUID`: 这是用来验证版本一致性的唯一标识符。如果不显式指定,默认情况下编译器会基于类的内容生成一个哈希。但是这样可能导致即使只是修改注释也会改变ID从而引发兼容性问题所以最好手动设定固定数。 - `transient` 关键字标注那些不想参与序列化进程里的属性。 另外还可以重载默认行为来自定义更复杂的逻辑处理方式比如覆盖特殊的方法像 `writeObject()` 和 `readObject()`, 它们会在执行相应动作之前自动调用[^2]: ```java private void writeObject(java.io.ObjectOutputStream out) throws IOException{ out.defaultWriteObject(); } private void readObject(java.io.ObjectInputStream in )throws IOException , ClassNotFoundException{ in .defaultReadObject(); } ``` 以上例子演示了怎样扩展基础功能以满足特定业务的需求。 --- ### 注意事项 尽管方便易用,但在实际开发工作中应谨慎考虑是否真的有必要让某类型具备此特性因为这可能会带来额外负担包括但不限于性能损耗、维护成本上升等问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值