详解 序列化流 与 反序列化流
(请观看本人博文——《详解 I/O流》)
序列化流 与 反序列化流
可能同学们看到本篇博文时会有如下问题:
什么是 序列化 与 反序列化 呢?
那么,现在,本人就来讲解下这两个名词的定义:
- 序列化:
就是把对象通过流的方式存储到文件中- 反序列化:
就是把文件中存储的对象以流的方式还原成对象
现在,本人再来介绍下这两个流的作用:
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,
可以为应用程序提供对对象图形的持久存储。
而且,被存储的对象的类必须继承java.io.Serializable接口,否则无法读取所存储的数据
在讲解这两个流之前,本人现要讲解下 transient 关键字:
transient 关键字:
被修饰的成员,在 不会被序列化。
所以当我们用序列化流去存储后,在读取时,所得到的结果 和 我们读取未赋值的成员的结果一样。
那么,现在,本人就分别对这两个流进行下讲解:
首先是 序列化流(ObjectOutputStream 类):
ObjectOutputStream 类:
(序列化流)
概述:
此类 是将 Java对象的基本数据类型 和 图形 写入 OutputStream
可以使用 ObjectInputStream 读取(重构)对象
通过在流中使用文件可以实现对象的持久存储
如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象
现在,本人来展示下这个类的构造方法:
构造方法:
- protected ObjectOutputStream()
为完全重新实现 ObjectOutputStream 的子类提供一种方法,
让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据- ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream
那么,本人来展示下这个类的 常用API:
常用API:
- protected void annotateClass(Class<?> cl)
子类可以实现此方法,从而允许在流中存储类数据- protected void annotateProxyClass(Class<?> cl)
子类可以实现此方法,从而在流中存储 定制数据 和 动态代理类的描述符- void close()
关闭流- void defaultWriteObject()
将当前类的非静态和非瞬态字段写入此流- protected void drain()
排空 ObjectOutputStream 中的所有已缓冲数据- protected boolean enableReplaceObject(boolean enable)
允许流对流中的对象进行替换- void flush()
刷新该流的缓冲- ObjectOutputStream.PutField putFields()
获取用于缓冲写入流中的持久存储字段的对象- protected Object replaceObject(Object obj)
在序列化期间,
此方法允许 ObjectOutputStream 的受信任子类使用一个对象替代另一个对象- void reset()
重置将丢弃已写入流中的所有对象的状态- void useProtocolVersion(int version)
指定要在写入流时使用的流协议版本- void write(byte[] buf)
写入一个 byte 数组- void write(byte[] buf, int off, int len)
写入字节的子数组- void write(int val)
写入一个字节- void writeBoolean(boolean val)
写入一个 boolean 值- void writeByte(int val)
写入一个 8 位字节- void writeBytes(String str)
以字节序列形式写入一个 String- void writeChar(int val)
写入一个 16 位的 char 值- void writeChars(String str)
以 char 序列形式写入一个 String- protected void writeClassDescriptor(ObjectStreamClass desc)
将指定的类描述符写入 ObjectOutputStream- void writeDouble(double val)
写入一个 64 位的 double 值- void writeFields()
将已缓冲的字段写入流中- void writeFloat(float val)
写入一个 32 位的 float 值- void writeInt(int val)
写入一个 32 位的 int 值- void writeLong(long val)
写入一个 64 位的 long 值- void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream- protected void writeObjectOverride(Object obj)
子类用于重写默认 writeObject 方法的方法- void writeShort(int val)
写入一个 16 位的 short 值- protected void writeStreamHeader()
提供 writeStreamHeader 方法,
这样子类可以将其自身的头部添加或预加到流中- void writeUnshared(Object obj)
将“未共享”对象写入 ObjectOutputStream- void writeUTF(String str)
以 UTF-8 修改版格式写入此 String 的基本数据
现在,本人来展示下部分API 的使用:
首先,本人给出一个用于存储粉丝信息的FanInfo:
package edu.youzg.about_io.about_file.core;
import java.io.Serializable;
public class FanInfo implements Serializable {
private String name;
//transient关键字 表示 某个成员变量不想序列化
private transient int age;
public FanInfo() {
}
public FanInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
现在,本人来编写一个测试类:
package edu.youzg.about_io.about_file.core.Test;
import edu.youzg.about_io.about_file.core.FanInfo;
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FanInfo FanInfo3 = new FanInfo("朝菌", 1);
FanInfo FanInfo1 = new FanInfo("南冥灵者", 500);
FanInfo FanInfo2 = new FanInfo("大椿", 8000);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("FanInfo1.txt"));
out.writeObject(FanInfo1);
out.writeObject(FanInfo2);
out.writeObject(FanInfo3);
out.close();
}
}
那么,现在,本人来展示下运行后的结果 以及 生成的文件的内容:
现在,本人来讲解下 反序列化流:
ObjectInputStream 类:
(反序列化流):
概述:
ObjectInputStream 用于恢复那些以前序列化的对象。
其他用途包括使用套接字流在主机之间传递对象,
或者用于编组和解组远程通信系统中的实参和形参。
ObjectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中显示的类相匹配。
使用标准机制按需加载类。
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
本人先来展示下这个类的 构造方法:
构造方法:
- protected ObjectInputStream()
为完全重新实现 ObjectInputStream 的子类提供一种方式,
让它不必分配仅由 ObjectInputStream 的实现使用的私有数据- ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream
现在,本人来展示下这个类的常用API:
常用API:
- int available()
返回可以不受阻塞地读取的字节数- void close()
关闭输入流- void defaultReadObject()
从此流读取当前类的非静态和非瞬态字段- protected boolean enableResolveObject(boolean enable)
使流允许从该流读取的对象被替代- int read()
读取数据字节- int read(byte[] buf, int off, int len)
读入 byte 数组- boolean readBoolean()
读取一个 boolean 值- byte readByte()
读取一个 8 位的字节- char readChar()
读取一个 16 位的 char 值- protected ObjectStreamClass readClassDescriptor()
从序列化流读取类描述符- double readDouble()
读取一个 64 位的 double 值- ObjectInputStream.GetField readFields()
按名称从流中读取持久字段并使其可用- float readFloat()
读取一个 32 位的 float 值- void readFully(byte[] buf)
读取字节,同时阻塞直至读取所有字节- void readFully(byte[] buf, int off, int len)
读取字节,同时阻塞直至读取所有字节- int readInt()
读取一个 32 位的 int 值- long readLong()
读取一个 64 位的 long 值- Object readObject()
从 ObjectInputStream 读取对象- protected Object readObjectOverride()
此方法由 ObjectOutputStream 的受信任子类调用,这些子类使用受保护的无参数构造方法构造 ObjectOutputStream- short readShort()
读取一个 16 位的 short 值- protected void readStreamHeader()
提供的 readStreamHeader 方法允许子类读取并验证它们自己的流头部- Object readUnshared()
从 ObjectInputStream 读取“非共享”对象- int readUnsignedByte()
读取一个无符号的 8 位字节- int readUnsignedShort()
读取一个无符号的 16 位 short 值- String readUTF()
读取 UTF-8 修改版格式的 String- void registerValidation(ObjectInputValidation obj, int prio)
在返回图形前注册要验证的对象- protected Class<?> resolveClass(ObjectStreamClass desc)
加载指定流类描述的本地等价类- protected Object resolveObject(Object obj)
在反序列化期间,此方法允许 ObjectInputStream 的受信任子类使用一个对象替代另一个- protected Class<?> resolveProxyClass(String[] interfaces)
返回一个代理类,该类实现在代理类描述符中命名的接口;
子类可以实现此方法,以便从流及动态代理类的描述符中读取自定义数据,允许它们使用接口和代理类的替换加载机制- int skipBytes(int len)
跳过字节String readLine()
已过时。 此方法不能正确地将字节转换为字符。请参见 DataInputStream 以获取详细信息和替代方法
那么,现在,本人来通过一个例子展示下部分API的使用:
现在,本人对上面代码生成的.txt文件来给出一个测试类:
package edu.youzg.about_io.about_file.core.Test;
import edu.youzg.about_io.about_file.core.FanInfo;
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objin = new ObjectInputStream(new FileInputStream("FanInfo1.txt"));
Object obj = objin.readObject();
FanInfo FanInfo= (FanInfo) obj;
System.out.println(FanInfo.getName());
obj = objin.readObject();
FanInfo = (FanInfo) obj;
System.out.println(FanInfo.getName());
obj = objin.readObject();
FanInfo = (FanInfo) obj;
System.out.println(FanInfo.getAge());
objin.close();
}
}
现在,本人来展示下运行结果:
可以看到:
我们所存储的每个Fan对象的age成员,由于我们使用了transient关键字来修饰。
所以当我们读取的时候,输出的是0
(各类型被transient关键字修饰后,读取到的结果 和 我们读取未赋值的成员 所得到的结果一样)
那么,现在,本人修改下FanInfo类 —— 将name成员改为public修饰符修饰的。
然后,再来运行下本人给出的测试类:
可以看到,出现了异常。
这是因为,我们存储该的对象信息 与 我们读取时的对象信息不同了。
其实,这个流检测的是该类的序列化版本号
其实,我们也可以通过自己设定个序列化版本号来克服这一点,
譬如,本人将FanInfo类改为如下:
package edu.youzg.about_io.about_file.core;
import java.io.Serializable;
public class FanInfo implements Serializable {
private static final long serialVersionUID = -7602640005373026150L;
private String name;
private transient int age;
public FanInfo() {
}
public FanInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
那么,本人再运行下之前生成文件的代码,然后再将FanInfo类中的name成员改为public修饰符修饰的,再来运行下如下代码:
import edu.youzg.about_io.about_file.core.FanInfo;
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objin = new ObjectInputStream(new FileInputStream("FanInfo2.txt"));
Object obj = objin.readObject();
FanInfo FanInfo= (FanInfo) obj;
System.out.println(FanInfo.getName());
obj = objin.readObject();
FanInfo = (FanInfo) obj;
System.out.println(FanInfo.getName());
obj = objin.readObject();
FanInfo = (FanInfo) obj;
System.out.println(FanInfo.getAge());
}
}
可以看到,读取没有问题!
(本人 I/O流总集篇 博文链接:https:////www.cnblogs.com/codderYouzg/p/12418404.html)