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


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

  • 相关阅读:
    (C/C++)区别:数组与指针,指针与引用
    C++中数组名和指针的区别联系
    C++引用的用处
    C++编写DLL动态链接库的步骤与实现方法
    C++_编写动态链接库
    C++ 模板
    C++ 信号处理
    C++ 多线程
    js事件冒泡
    js事件委托
  • 原文地址:https://www.cnblogs.com/codderYouzg/p/12418690.html
Copyright © 2020-2023  润新知