• 一、IO流的概念
  • 二、IO流的分类
    • 1.输入流和输出流
    • 2.字节流和字符流
    • 3.节点流和处理流
  • 三、IO流的四大基类
    • 1.InputStream
    • 2.Reader
  • 3.OutputStream
  • 4.Writer

    一、IO流的概念

    Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出源抽象表述为”流”。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
    流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。

    二、IO流的分类

    1.输入流和输出流

    根据数据流向不同分为:输入流和输出流。

    输入流:只能从中读取数据,而不能向其写入数据。
    输出流:只能向其写入数据,而不能从中读取数据。

    如下如所示:对程序而言,向右的箭头,表示输入,向左的箭头,表示输出。
    Java IO(二) - 图1

    2.字节流和字符流

    字节流和字符流和用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同。
    字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
    (1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    (2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

    只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    3.节点流和处理流

    按照流的角色来分,可以分为节点流和处理流。
    可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被成为低级流。
    处理流是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流。

    1. //节点流,直接传入的参数是IO设备
    2. FileInputStream fis = new FileInputStream("test.txt");
    3. //处理流,直接传入的参数是流对象
    4. BufferedInputStream bis = new BufferedInputStream(fis);

    Java IO(二) - 图2
    当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入/输出节点连接。使用处理流的一个明显好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应地发生变化。
    实际上,Java使用处理流来包装节点流是一种典型的装饰器设计模式,通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。

    三、IO流的四大基类

    根据流的流向以及操作的数据单元不同,将流分为了四种类型,每种类型对应一种抽象基类。这四种抽象基类分别为:InputStream,Reader,OutputStream以及Writer。四种基类下,对应不同的实现类,具有不同的特性。在这些实现类中,又可以分为节点流和处理流。下面就是整个由着四大基类支撑下,整个IO流的框架图。
    Java IO(二) - 图3
    InputStream,Reader,OutputStream以及Writer,这四大抽象基类,本身并不能创建实例来执行输入/输出,但它们将成为所有输入/输出流的模版,所以它们的方法是所有输入/输出流都可以使用的方法。类似于集合中的Collection接口。

    1.InputStream

    InputStream 是所有的输入字节流的父类,它是一个抽象类,主要包含三个方法:

    1. //读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
    2. int read()
    3. //读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
    4. int read(byte[] buffer)
    5. //读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。
    6. int read(byte[] buffer, int off, int len)

    2.Reader

    Reader 是所有的输入字符流的父类,它是一个抽象类,主要包含三个方法:

    1. //读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
    2. int read()
    3. //读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。
    4. int read(char[] cbuf)
    5. //读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。
    6. int read(char[] cbuf, int off, int len)

    对比InputStream和Reader所提供的方法,就不难发现两个基类的功能基本一样的,只不过读取的数据单元不同。

    在执行完流操作后,要调用close()方法来关系输入流,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。

    除此之外,InputStream和Reader还支持如下方法来移动流中的指针位置:

    1. //在此输入流中标记当前的位置
    2. //readlimit - 在标记位置失效前可以读取字节的最大限制。
    3. void mark(int readlimit)
    4. // 测试此输入流是否支持 mark 方法
    5. boolean markSupported()
    6. // 跳过和丢弃此输入流中数据的 n 个字节/字符
    7. long skip(long n)
    8. //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
    9. void reset()

    3.OutputStream

    OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法:

    1. //向输出流中写入一个字节数据,该字节数据为参数b的低8位。
    2. void write(int b) ;
    3. //将一个字节类型的数组中的数据写入输出流。
    4. void write(byte[] b);
    5. //将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。
    6. void write(byte[] b, int off, int len);
    7. //将输出流中缓冲的数据全部写出到目的地。
    8. void flush();

    4.Writer

    Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:

    1. //向输出流中写入一个字符数据,该字节数据为参数b的低16位。
    2. void write(int c);
    3. //将一个字符类型的数组中的数据写入输出流,
    4. void write(char[] cbuf)
    5. //将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。
    6. void write(char[] cbuf, int offset, int length);
    7. //将一个字符串中的字符写入到输出流。
    8. void write(String string);
    9. //将一个字符串从offset开始的length个字符写入到输出流。
    10. void write(String string, int offset, int length);
    11. //将输出流中缓冲的数据全部写出到目的地。
    12. void flush()

    可以看出,Writer比OutputStream多出两个方法,主要是支持写入字符和字符串类型的数据。

    使用Java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)

    以上内容就是整个IO流的框架介绍。