详解 序列化流 与 反序列化流

Posted codderyouzg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解 序列化流 与 反序列化流相关的知识,希望对你有一定的参考价值。

(请观看本人博文——《详解 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

以上是关于详解 序列化流 与 反序列化流的主要内容,如果未能解决你的问题,请参考以下文章

Java对象操作流:序列化与反序列化

java对象序列化与反序列化

Java基础IO流序列化与反序列化

IO流相关知识(File,字节流,字符流,特殊操作流(标准输入流,标准输出流,对象序列化与反序列化,properties与IO流结合))相关知识总结

java序列化与反序列化流的操作,打印流的输出

对象输入输出流ObjectInputStreamObjectOutputStream(对象序列化与反序列化)