整理 Java I/O (四):针对对象的输入/输出流 - ObjectInput/OutputStream

Java 提供了针对对象的输入输出流(ObjectInputStream/OutputStream),在了解对象输入输出流之前,先了解一下序列化。

序列化与反序列化

含义

简而言之,序列化是将对象或者是数据结构的状态信息转换为可存储或者可传输形式的过程。
反序列化是将在序列化过程中存储的或者传输过来的信息还原成对象或者数据结构的过程。

为什么要序列化和发序列化

序列化对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到硬盘中,可以在不同的程序之间共享对象。我们可以将对象序列化到流、磁盘、内存和网络上。

实现序列化的要求

只有实现了Serializable或Externalizable接口的类的对象才能被序列话,否则会抛出“java.io.NotSerializableException”。

ObjectOutputStream

可以通过一系列的writeXXX方法将Java对象的基本数据类型和对象输入到 ObjectOutputStream 中中,同时可以通过在流中使用一个FileOutputStream来将基本数据或者对象持久化。

原始数据(不包括 serializable 字段和 externalizable 数据)以块数据记录的形式写入到 ObjectOutputStream 中。块数据由头部和数据组成。块数据头部分由标记和跟在后面的字节数组成。连续的原始数据会被写入到一个大小为2014字节的数据块中,数据块被填满或者被终止时写入到流中,调用ObjectOutputStream的writeObject、defaultWriteObject和writeFields方法可以终止现有的数据块。

需要写入流中的对象需要实现Serializable接口,否则会抛出异常。

构造方法

  • ObjectOutputStream()

    为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
  • ObjectOutputStream(OutputStream out)

    创建写入指定 OutputStream 的 ObjectOutputStream。

其他方法

  • close()

    关闭流。
  • defaultWriteObject()

    将当前类的非静态和非瞬态字段写入此流。
  • flush()

    刷新该流的缓冲。
  • putFields()

    获取用于缓冲写入流中的持久存储字段的对象。
  • reset()

    重置将丢弃已写入流中的所有对象的状态。
  • useProtocolVersion(int version)

    指定要在写入流时使用的流协议版本。
  • write(byte[] buf)

    写入一个 byte 数组。
  • write(byte[] buf, int off, int len)

    写入字节的子数组。
  • write(int val)

    写入一个字节。
  • writeBoolean(boolean val)

    写入一个 boolean 值。
  • writeByte(int val)

    写入一个 8 位字节。
  • writeBytes(String str)

    以字节序列形式写入一个 String。
  • writeChar(int val)

    写入一个 16 位的 char 值。
  • writeChars(String str)

    以 char 序列形式写入一个 String。
  • writeDouble(double val)

    写入一个 64 位的 double 值。
  • writeFields()

    将已缓冲的字段写入流中。
  • writeFloat(float val)

    写入一个 32 位的 float 值。
  • writeInt(int val)

    写入一个 32 位的 int 值。
  • writeLong(long val)

    写入一个 64 位的 long 值。
  • writeObject(Object obj)

    将指定的对象写入 ObjectOutputStream。
  • writeObjectOverride(Object obj)

    子类用于重写默认 writeObject 方法的方法。
  • writeShort(int val)

    写入一个 16 位的 short 值。
  • writeUnshared(Object obj)

    将“未共享”对象写入 ObjectOutputStream。
  • writeUTF(String str)

    以 UTF-8 修改版格式写入此 String 的基本数据。

ObjectInputStream

ObjectInputStream 对以前使用ObjectOutputStream写入的基本数据或者对象进行反序列化。通过一系列的readXXX方法去恢复以前那些序列化的对象,在读回的时候需要按照写入对象时相同的类型和顺序读取。

构造方法

  • ObjectInputStream()

    为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
  • ObjectInputStream(InputStream in)

    创建从指定 InputStream 读取的 ObjectInputStream。

其他方法

  • available()

    返回可以不受阻塞地读取的字节数。
  • close()

    关闭输入流。
  • defaultReadObject()

    从此流读取当前类的非静态和非瞬态字段。
  • read()

    读取数据字节。
  • read(byte[] buf, int off, int len)

    读入 byte 数组。
  • readBoolean()

    读取一个 boolean 值。
  • readByte()

    读取一个 8 位的字节。
  • readChar()

    读取一个 16 位的 char 值。
  • readDouble()

    读取一个 64 位的 double 值。
  • readFields()

    按名称从流中读取持久字段并使其可用。
  • readFloat()

    读取一个 32 位的 float 值。
  • readFully(byte[] buf)

    读取字节,同时阻塞直至读取所有字节。
  • readFully(byte[] buf, int off, int len)

    读取字节,同时阻塞直至读取所有字节。
  • readInt()

    读取一个 32 位的 int 值。
  • readLong()

    读取一个 64 位的 long 值。
  • readObject()

    从 ObjectInputStream 读取对象。
  • readShort()

    读取一个 16 位的 short 值。
  • readUnshared()

    从 ObjectInputStream 读取“非共享”对象。
  • readUnsignedByte()

    读取一个无符号的 8 位字节。
  • readUnsignedShort()

    读取一个无符号的 16 位 short 值。
  • readUTF()

    读取 UTF-8 修改版格式的 String。
  • registerValidation(ObjectInputValidation obj, int prio)

    在返回图形前注册要验证的对象。
  • skipBytes(int len)

    跳过字节。

简单示例

public class ObjectStreamTest {

    private ObjectInputStream ois;
    private ObjectOutputStream oos;

    private FileInputStream fis;
    private FileOutputStream fos;

    public static void main(String[] args) {

        ObjectStreamTest ost = new ObjectStreamTest();
        ost.testObjectOutputStream(new File("D:\\ObjectFile.dat"));
        ost.testObjectInputStream(new File("D:\\ObjectFile.dat"));

    }

    private void testObjectOutputStream(File file) {
        try {
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);

            oos.write("abcde".getBytes());// 写入一个字节数组
            oos.write("fghijk".getBytes(), 0, 5);// 写入部分字节数组
            oos.write(107); // 写入一个字节
            oos.writeBoolean(false);// 写入一个布尔值
            oos.writeByte(108);// 写入一个8位字节
            oos.writeBytes("mno");// 以字节形式写入一个字符串
            oos.writeChar(112);// 写入一个 16 位的 char 值。
            oos.writeChars("qrst");// 以 char 序列形式写入一个 String。
            oos.writeDouble(100.1);// 写入一个double值
            oos.writeFloat(200);// 写入一个float值
            oos.writeInt(300);// 写入一个int值
            oos.writeLong(400);// 写入一个long值

            Person person = new Person("张三", 10, "男", "No.10000");
            oos.writeObject(person);// 写入一个对象

            oos.writeShort(500);// 写入一个short值
            oos.writeUTF("This is a String");// 以UTF格式写入一个字符串
            oos.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private void testObjectInputStream(File file) {
        try {
            fis = new FileInputStream(file);
            ois = new ObjectInputStream(fis);

            for (int i = 0; i < 10; i++) {
                System.out.print((char) ois.read());// 读入数据字节
            }

            System.out.println("\n" + ois.available());

            System.out.print((char) ois.readByte());
            System.out.println("\n" + ois.readBoolean());

            ois.skip(1);

            for (int i = 0; i < 3; i++) {
                System.out.print((char) ois.read());
            }
            for (int i = 0; i < 5; i++) {
                System.out.print(ois.readChar());
            }

            System.out.println("\n" + ois.readDouble());
            System.out.println(ois.readFloat());
            System.out.println(ois.readInt());
            System.out.println(ois.readLong());

            Person person = (Person) ois.readObject();
            System.out.println(person.toString());

            System.out.println(ois.readShort());
            System.out.println(ois.readUTF());

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}