25、IO包中的对象输入输出流
程序员文章站
2022-04-03 20:09:22
...
一、ObjectOutputStream
- ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream
- 可以使用 ObjectInputStream 读取(重构)对象。
- 通过在流中使用文件可以实现对象的持久存储。
- 如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
- 只能将支持 java.io.Serializable 接口的对象写入流中。
- 每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { //创建写入指定 OutputStream 的 ObjectOutputStream public ObjectOutputStream(OutputStream out) throws IOException{} //将指定的对象写入 ObjectOutputStream。对象的类、类的签名,以及类及其所有超类型的非瞬态和非静态字段的值都将被写入。 public final void writeObject(Object obj) throws IOException{} //以 UTF-8 修改版格式写入此 String 的基本数据。 //注意,将 String 作为基本数据写入流中与将它作为 Object 写入流中明显不同。 //由 writeObject 写入的 String 实例最初是作为 String 写入流中的。然后,writeObject() 调用将对该字符串的引用写入流中。 public void writeUTF(String str) throws IOException{} }
二、java.io.Serializable接口
- public interface Serializable
- 类通过实现 java.io.Serializable 接口以启用其序列化功能。
- 未实现此接口的类将无法使其任何状态序列化或反序列化。
- 可序列化类的所有子类型本身都是可序列化的。
- 序列化接口没有方法或字段,仅用于标识可序列化的语义。
- 当遍历一个图形时,可能会遇到不支持 Serializable 接口的对象。
- 在此情况下,将抛出 NotSerializableException,并将标识不可序列化对象的类。
- writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以恢复它
- 序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,
- 该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。
- 如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,
- 则反序列化将会导致 InvalidClassException。
- 可序列化类可以通过声明名为 "serialVersionUID" 的字段
- 该字段必须是静态 (static)、最终 (final) 的 long 型字段,显式声明其自己的 serialVersionUID
- 如:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
- 如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,
- 不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,
- 原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,
- 根据编译器实现的不同可能千差万别,
- 这样在反序列化过程中可能会导致意外的 InvalidClassException。
- 因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,
- 序列化类必须声明一个明确的 serialVersionUID 值。
- 还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),
- 原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。
- 数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,
- 但是数组类没有匹配 serialVersionUID 值的要求。
- 没有方法的接口叫做标记接口 相当于加个标记
- 静态成员不能被序列化,因为它存放在方法区,那么想让堆中的成员也不被序列化可以加个修饰符:transient
三、ObjectInputStream
- ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
- ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,
- 可以为应用程序提供对对象图形的持久存储。
- ObjectInputStream 用于恢复那些以前序列化的对象。
- 其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
- 默认情况下,对象的反序列化机制会将每个字段的内容恢复为写入时它所具有的值和类型。
- 反序列化进程将忽略声明为瞬态或静态的字段。
- 对其他对象的引用使得根据需要从流中读取这些对象。
- 使用引用共享机制能够正确地恢复对象的图形。
- 反序列化时始终分配新对象,这样可以避免现有对象被重写。
- 读取对象类似于运行新对象的构造方法。
- 为对象分配内存并将其初始化为零 (NULL)。
- 为不可序列化类调用无参数构造方法,然后从以最接近 java.lang.object 的可序列化类开始和以对象的最特定类结束的流恢复可序列化类的字段。
- 其构造方法与方法均与ObjectOutputStream一一对应
四、示例
import java.io.*; public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { writeObj(); readObj(); } public static void readObj() throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream("e:/p.object"); ObjectInputStream ois = new ObjectInputStream(fis); Person p = (Person)ois.readObject(); System.out.println(p); } public static void writeObj() throws IOException { FileOutputStream fos = new FileOutputStream("e:/p.object"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(new Person("zhangsan",20)); oos.close(); System.out.println("已成功保存对象"); } } class Person implements Serializable { String name; int age; transient String sex = "man"; Person(String name,int age) { this.name = name; this.age = age; } public String toString() { return name+":"+age+":"+sex; } } //结果为:zhangsan:20:null如果把transient改为static 结果为:zhangsan:20:man
下一篇: python如何在循环中获取下标
推荐阅读