java I/O体系总结

Posted by 梧桐和风的博客 on September 10, 2018

java I/O体系总结

I/O流的理解

先看看流的概念

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

通俗的说,有两个文件A和B,想要把A的内容拷贝到B中,可以假设两文件间有一个通道,把A的数据按字节或是字符的形式传送给B。这个通道就是java I/O体系流的概念,即可以把流当做抽象的通道。

理解了流的概念,流的分类也就很好明白了。以流的方向来说,流从外界(网络或磁盘)读取到程序(内存)中,就是输入流。流从程序流向外界就是输出流。

这里写图片描述

基础的流

流又可按传输的数据类型分为字节流和字符流。简单理解就是传输是二进制的字节(字节流)还是直接可以看懂的字符(字符流)。结合输入输出流,java的I/O流基础的类(接口)主要有以下几个:

  1. InputStream(输入字节流)
  2. OutputStream(输出字节流)
  3. Reader(输入字符流)
  4. Writer(输出字符流)

关于字节流

字节流是最基本的I/O处理流,之所以基本,是因为所有形式的储存(文件,磁盘或网络)底层都是以字节形式存储的。InputStream是所有字节输入流的父类,OutputStream是所有字节输出流的父类。字节流主要用于处理二进制数据。处理单位主要是字节或字节数组。

关于字符流

鉴于有些文件是以文本存储的,为方便操作,于是有了字符流。字符流主要用于处理字符或字符串。每个字符占两个字节。在实现上,字符流即由java虚拟机将字节转为字符(每2个字节转为一个字符)形成的。字符输入流的父类是Writer,字符输出流的父类是Reader。

示例

其他流都是基于以上4个基础接口做的扩展及实现。就以文件传输为例,传输类型为字节,则有FileInputStream(文件输入流)和FileOutputStream(文件输出流)。看下示例:


   /**
     * 输入流测试
     * 将磁盘中的文件读取到内存中,以字节的形式
     * 要读取的文件为hello.txt,其中内容为"hello,world"
     */
    @Test
    public void testInputStream() throws Exception {
        InputStream inputStream = null;
        try {
            //代表磁盘文件
            File file = new File("/Users/wangtonghe/local/tmp/hello.txt");
            // 构建输入流
            inputStream = new FileInputStream(file);
            int b = 0;
            // 获取到流的内容
            while ((b = inputStream.read()) != -1) {
                // 输出流的内容
                System.out.println((b);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }


上面这个示例很简单,就是将待操作文件包装到文件输入流中,读取到内存。

看一个整体的示例吧,字符类型的文件输入输出。将a.txt的内容拷贝到b.txt中。FileWriter表示文件字符输出类,FileReader表示文件字符输入类。

一般的做法是,将a.txt包装到文件输入流,读取到内存。再通过文件输出流输出到b.txt文件中。

用图表述即为

这里写图片描述


    @Test
    public void testFile() throws Exception {

        File aFile = new File("/Users/wangtonghe/local/tmp/a.txt");
        File bFile = new File("/Users/wangtonghe/local/tmp/b.txt");

        FileReader reader = null;
        FileWriter writer = null;

        try {
            // 构建文件输入流
            reader = new FileReader(aFile);
            // 构建文件输出流
            writer = new FileWriter(bFile);
            char[] chars = new char[1024];
            int len = 0;
            // 读取输入流
            if ((len = reader.read(chars)) != -1) {
                //写入输出流
                writer.write(chars, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                reader.close();
            }
            if (writer != null) {
                writer.close();
            }
        }
    }

字节流与字符流的区别

  1. 操作对象不同,这是最基本的区别。字节流操作字节或字节数组;字符流操作字符或字符串。
  2. 字节流对终端(文件等)直接进行操作,而字符流使用缓冲区(即先把数据写入缓存区,再对缓存区进行操作)
  3. 由于2的存在,对字节的操作会直接影响终端(文件)结果,而对字符流的操作最后必须关闭流或强制刷新流才能写入数据,否则只是写到了缓存区,文件等终端不受影响。
  4. 详见 java 字节流与字符流的区别

两种流的相互转化

InputStreamReader以及OutputStreamWriter 负责两种流的相互转换。(一般是字节流转字符流,没有字符转字节,也不需要)

InputStreamReader可以将字节输入流转为字符输入流;OutputStreamWriter可以将字节输出流转为字符输出流。

转化过程如下:


 //文本文件
 File aFile = new File("/Users/wangtonghe/local/tmp/a.txt");
 // 文件字节流
 FileInputStream fileInputStream = new FileInputStream(aFile);
 // 字节流转为字符流
 Reader reader = new InputStreamReader(fileInputStream);

转化流构造方法及用法如下

  1. InputStreamReader(InputStream); //通过构造函数初始化,使用的是本系统默认的编码表GBK。
  2. InputStreamWriter(InputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
  3. OutputStreamWriter(OutputStream); //通过该构造函数初始化,使用的是本系统默认的编码表GBK。
  4. OutputStreamwriter(OutputStream,String charSet); //通过该构造函数初始化,可以指定编码表

I/O流概览

这里写图片描述

java体系I/O流大致类及结构就如上图所示。

简要介绍一下

输入字节流InputStream

  1. InputStream 是所有的输入字节流的父类,它是一个抽象类。
  2. ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte数组、StringBuffer、和本地文件中读取数据。
  3. ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

同理输出字节流OutputStream与此类似

  1. OutputStream 是所有的输出字节流的父类,它是一个抽象类。
  2. ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
  3. ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。

I/O体系的装饰器模式

关于I/O体系,一定要谈的就是其装饰器的设计模式。

装饰器模式

概念 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。它能让我们在扩展类的时候让系统较好的保持灵活性。

这里就不具体介绍装饰器模式了,可以看看Java设计模式12:装饰器模式

java I/O中实现装饰器模式主要由FilterInputStream及其子类(输入流)和FilterOutputStream及其子类完成。

如BufferedInputStream,有缓冲功能的输入流,可修饰FileIntputStream使其拥有缓冲功能。

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(“/home/user/abc.txt”)));

参考文章

  1. java 字节流与字符流的区别
  2. 字节流与字符流的区别详解
  3. Java IO流学习总结一:输入输出流
  4. Java设计模式12:装饰器模式
  5. Java IO : 流,以及装饰器模式在其上的运用