【Java学习笔记十】——I/O流之对象流

声明:本文章内容主要摘选自尚硅谷宋红康Java教程、《Java核心卷二》、廖雪峰Java教程,示例代码部分出自本人,更多详细内容推荐直接观看以上教程及书籍,若有错误之处请指出,欢迎交流。

I/O流的笔记附带比较长的实例代码,所以分开几篇文章发,本篇为最后一篇,由于本篇对象流的内容在上述教程的描述较少,我摘选了一篇CSDN的文章对象流,感兴趣的可以去看看

1.对象流的概念

对象流是和字节流/字符流同处于一个概念体系的:

​ a. 这么说字节流是流动的字节序列,字符流是流动的字符序列,那么对象流就是流动的对象序列咯?

​ b. 概念上确实可以这样理解,对象流就是专门用来传输Java对象的;

​ c. 但是字节和字符都是非常直观的二进制码(字节本身就是,而字符是一种二进制编码),二进制码的流动是符合计算机的概念模型的,可是对象是一个抽象的东西,对象怎么能像二进制码那样流动呢?

​ d. 其实很好理解,对象流只不过是Java的API而已,表象上(方法调用等)流动的是对象,而实际上在底层肯定都是转换成二进制码流动的;

​ e. 具体来说底层是将对象转换成平台无关的Java字节流进行传播的,以为对象流的类名就是ObjectInputStream和ObjectOutputStream,以stream作为后缀必然传递的是字节流;

​ f. 只不过在调用对象流的read和write系列方法时不用将对象转换成字节数组传入了,而是可以直接传入对象本身,这些方法内部会自动将对象转化成字节流传递。

2.为什么需要对象流

​ i. 首先,Java本身就是一个面向对象的语言,因此对象比字节/字符的使用更广泛;

​ ii. 传递文本当然使用字符流,因此字符流的使用很广泛,即文本、字符串的处理、保存等应用很广,这毋庸置疑,而直接传递字节的应用(比如图像、音频、二进制文件等)可能也非常广泛,但是Java不仅仅是用来处理这两种数据的,Java真正面对最多的还是对象;

​ iii. 程序往往需要在各个存储节点间传递Java对象(即Java对象的输入输出),按照传统方法要么就是使用字节流来传递要么就是用字符流来传递:

​ a. 首先字符流可以排除,因为对象中可能既包含文本型数据(String等),也可能包含非文本类数据(比如字节、图像等),如果将图像这样的数据也转换成字符的话显然是行不通的;

​ b. 那就只能使用字节流了,但是字节流的使用很麻烦,需要自己手动将对象内所有非字节型数据现转换成字节数据,然后将所有转换成字节的成员全部挤进一个字节数组写入(输出),而输入的时候必须先用一整个字节数组读取,然后对数组进行解析,最后再还原成原来的对象。

3.序列化

  1. 不是随便调用对象流的read和write就能随随便便输入输出一个对象的,前提必须是这个对象是可序化/可反序列化的!

  2. 即对象流必须知道这个对象该如何序列化以及反序列化,才能正确对该对象进行输入输出;

  3. Serializable接口:

​ i. 必须实现该接口才能自动序列化和反序列化;

​ ii. 该接口有两个要实现的方法,反别对应如何序列化和反序列化:

​ a. 序列化的算法实现:private void writeObject(ObjectOutputStream out) throws IOException;

​ b. 反序列化的算法实现:private Object readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

!!可以看到反序列化时并不是用构造器构造的,而是直接根据输入流生成一坨无类型的数据,然后用强制类型转换转换出来的!因此可以看到该方法会抛出ClassNotFoundException,如果没有准备好相应的类型则会抛出该异常;

!!对象流输入输出底层其实是这样调用的:

​ a. ObjectOutputStream oos:oos.writeObject(obj) -> obj.wirteObject(oos)

​ b. ObjectInputStream ois:ois.readObject(obj) -> obj.readObject(ois)

!!可以看到,实现的算法中就是利用obj得到的oos和ois进行输入输出;

  1. 毕竟有些读写并不是规规矩矩地原模原样地读写,比如像密码这样的信息,在输出的时候往往需要加密输出,因此读取的时候也要解密读取,像这样的情况就必须自己定义序列化和反序列化的算法了;

  2. 绝大多数的Java基础类,比如String、Date等都实现了Serializable接口,因此可以直接用对象流的readObject和writeObject读写;

  3. 所有的基础类型(int、double、boolean等)对象流也提供了相应的readXxx和writeXxx进行序列化和反序列化,因此也不用担心;

  4. 因此大多数的自定义类型的对象就要自己实现序列化和反序列化的算法了,而上面提供的基础类型的对象流输入输出就是为自定义准备的,例如以下:

/*自定义类 需要满足如下的要求,方可序列化
1.需要实现接口:Serializable
2.当前类提供一个全局常量:serial VersionUID(这个UID可以去Serializable接口的说明中获取,但是具体数字需要有所修改)
如:static final long serialVersionUID = 4829398432L;
3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。
(默认情况下,基本数据类型可序列化)
*/
class Person implements Serializable {
    private String name;
    private int age;

    static final long serialVersionUID = 4829398432L;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

4.实例

@Test
    public void test1(){
        ObjectOutputStream oos = null;

        try{
            oos = new ObjectOutputStream(new FileOutputStream("a.txt"));

            oos.writeObject(new Person("李一", 18));
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @Test
    public void test2(){
        ObjectInputStream ois = null;

        try{
            ois = new ObjectInputStream(new FileInputStream("a.txt"));

            Person p = (Person)ois.readObject();

            System.out.println(p.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally{
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

此笔记仅针对有一定编程基础的同学,且本人只记录比较重要的知识点,若想要入门Java可以先行观看相关教程或书籍后再阅读此笔记。

最后附一下相关链接:
Java在线API中文手册
Java platform se8下载
尚硅谷Java教学视频
《Java核心卷二》

posted @ 2020-08-21 16:07  洛水凌云  阅读(133)  评论(0编辑  收藏  举报