仓库源文站点原文


title: Java中的IO流 toc: true date: 2019-11-25 22:30:49 cover: https://img.paulzzh.com/touhou/random?5 categories: IO基础 tags: [Java基础]

description: Java中的IO一直都是我的薄弱项, 本篇总结了Java中的IOStream

Java中的IO一直都是我的薄弱项, 所以今天就来总结一下Java中的IOStream

<br/>

<!--more-->

一. 流的概念和作用

<font color="#ff0000">流: 代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象</font>

流的本质: 数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作

作用: 为数据源和目的地建立一个输送通道

<br/>

1. Java IO所采用的模型

Java的IO模型使用Decorator(装饰者)模式,按功能划分Stream,所以可以动态装配这些Stream,以便获得需要的功能

例如: 需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

<br/>

2. IO流的分类

① 按数据流的方向分为: 输入流和输出流(此输入、输出是相对于我们写的代码程序而言):

② 按处理数据单位分为字节流和字符流(1字符 = 2字节, 一个汉字占两个字节长度):

③ 按功能分为节点流和处理流:

<br/>

小贴士: 处理流的构造方法总是要带一个其他的流对象做参数, 一个流对象可以经过其他流的多次包装

<br/>

4个基本的抽象流类型,所有的流都继承这四个:

① InputStream: 字节输入流

InputStream.png

② OutputStream:字节输出流

OutputStream.png

③ Reader:字符输入流

Reader.png

④ Writer:字符输出流

Writer.png

四个基本抽象流的关系如下表:

输入流 输出流
字节流 InputStream OutPutStream
字符流 Reader Writer

<br/>

小结: 如何选择合适的IO流

① 首先要选择输入流还是输出流;

② 然后考虑传输数据时,是选择使用字节流传输还是字符流,也就是每次传1个字节还是2个字节,有中文肯定就选择字符流了

③ 前面两步就可以选出一个合适的节点流了,比如字节输入流inputStream,如果要在此基础上增强功能,那么就在处理流中选择一个合适的即可

<br/>

3. IO流特性

先进先出(FIFO),最先写入输出流的数据最先被输入流读取到

顺序存取,可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据(RandomAccessFile可以从文件的任意位置进行存取(输入输出)操作)

③ 只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作

<br/>

注: 在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流

<br/>

4. IO流常用到的类

在整个Java.io包中最重要的几个类指的是:

整个IO流的结构如下图:

整个IO流的结构.png

<br/>

5. Java IO流对象

① 输入字节流InputStream

InputStream.png

<br/>

② 输出字节流OutputStream

OutputStream.png

OutputStream 是所有的输出字节流的父类,它是一个抽象类

<br/>

③ 字符输入流Reader

Reader.png

在上面的继承关系图中可以看出:

<br/>

小结: Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致

<br/>

④ 字符输出流Writer

Writer.png

在上面的关系图中可以看出:

<br/>

小结: 字节流和字符流的使用场景

字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件

<br/>

6. 字符流与字节流转换

① 转换流的作用

<font color="#ff0000">例如: 文本文件在硬盘中以字节流的形式存储时,通过InputStreamReader读取后转化为字符流给程序处理,程序处理的字符流通过OutputStreamWriter转换为字节流保存</font>

<br/>

② 转换流的特点

<br/>

③ 何时使用转换流

<br/>

④ 具体的对象体现

<br/>

说明: 这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来

<br/>

7. 字节流和字符流的区别

字节流是最基本的,所有的InputStream和OutputStream的子类都是字节流, 主要用在处理二进制数据,它是按字节来处理的

但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化这两个之间通过 InputStreamReader,OutputStreamWriter来关联(实际上是通过byte[]和String来关联)

<br/>

小贴士: 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的

在从字节流转化为字符流时,实际上就是byte[]转化为String时,public String(byte bytes[], String charsetName)有一个关键的参数字符集编码:

如果我们省略了,那系统就用操作系统的lang而在字符流转化为字节流时,实际上当String转化为byte[]时: byte[] String.getBytes(String charsetName)也是一样的道理

<br/>

小结:

① 字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的

因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在若未将缓冲区装满、调用close()方法关闭缓冲区时或者手动调用flush()方法刷新缓冲区,信息才输出;

② 读写单位不同:**字节流以字节(8bit)为单位,字符流以字符为单位**,根据码表映射字符,一次可能读多个字节

③ 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据

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

<br/>

8. System类对IO的支持

在System类中提供了以下的几个静态对象:

名称 描述
public static final PrintStream out 对应标准输出, 显示器
public static final PrintStream err 对应错误输出, 显示器
public static final InputStream in 对应标准输入, 键盘

<br/>

备注: 标准I/O

Java程序可通过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而通过文件可以与外界进行任意数据形式的信息交换

<br/>

二. IOStream中的装饰流

BufferedReader, BufferedWriter,BufferedInputStream, BufferedOutputsStream,都要包上一层节点流

也就是说装饰流是在节点流的基础之上进行的,而带有Buffered的流又称为缓冲流,缓冲流处理文件的输入输出的速度是最快的, 所以一般缓冲流的使用比较多

1. 什么是装饰者模式?

装饰者中拥有被装饰者的实例,然后有什么具体的装饰我们都另写一个类来继承该装饰者,当我们需要该装饰时,就new出该类来,然后将其被装饰者当作参数传递进去(类似于代理模式)

装饰者模式.png

现在来看看一个具体的实例: 我们需要制作一份鸡腿堡,流程是怎样的呢?

① 先有基本原料,也就是两块面包,这是不管做什么汉堡都需要的

② 做什么汉堡,取决于加什么材料,比如生菜,鸡肉等,所以根据材料来做汉堡,想做什么汉堡就加什么材料

③ 所有材料加完之后,直接计算价格即可

换一种汉堡,也不需要改源码,什么也不需要, 只要将汉堡传入不同的构造器即可

装饰者模式2.png

<br/>

2. Scanner类

Java 5添加了java.util.Scanner类,这是一个用于扫描输入文本的类, 它是以前的StringTokenizer和Matcher类之间的某种结合

由于任何数据都必须通过同一模式的捕获组检索或通过使用一个索引来检索文本的各个部分, 于是可以结合使用正则表达式和从输入流中检索特定类型数据项的方法。除了能使用正则表达式之外,Scanner类还可以任意地对字符串和基本类型(如int和double)的数据进行分析

<br/>

小贴士: 借助于Scanner,可以针对任何要处理的文本内容编写自定义的语法分析器

Scanner套接字节流或字符流:

<br/>

<br/>

总结:

① InputStream类的功能不足被Scanner解决了

② OutputStream类的功能不足被PrintStream解决了

③ Reader类功能不足被BufferReader解决了

④ Writer类的功能不足被PrintWriter解决了

输出数据用PrintStream, Printwriter

读取数据用Scanner其次是BufferReader

<br/>