Android开发中你所须要掌握的Java IO框架,看这一篇就够了

做者:享学Alvin老师

1、IO框架

Java IO 的学习是一件很是艰巨的任务。java

它的挑战是来自于要覆盖全部的可能性。不只存在各类I/O源端还有想要和他通讯的接收端(文件/控制台/网络连接),并且还须要以不一样的方式与他们进行通讯(顺序/随机存取/缓冲/二进制/字符/行/字 等等)这些状况综合起来就给咱们带来了大量的学习任务,大量的类须要学习。json

咱们要学会全部的这些java 的IO是很难的,由于咱们没有构建一个关于IO的体系,要构建这个体系又须要深刻理解IO库的演进过程,因此,咱们若是缺少历史的眼光,很快咱们会对何时应该使用IO中的哪些类,以及何时不应使用它们而困惑。设计模式

因此,在开发者的眼中,IO很乱,不少类,不少方法,很迷茫。数组

2、IO简介

数据流是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。缓存

流序列中的数据既能够是未经加工的原始二进制数据,也能够是经必定编码处理后符合某种格式规定的特定数据。所以Java中的流分为两种:安全

  1. 字节流:数据流中最小的数据单元是字节bash

  2. 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。网络

Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。框架

Java I/O主要包括以下3层次:dom

  1. 流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等

  2. 非流式部分——如:File类、RandomAccessFile类和FileDescriptor等类

  3. 其余——文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操做系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。


在Android 平台,从应用的角度出发,咱们最须要关注和研究的就是 字节流(Stream)字符流(Reader/Writer)和 File/ RandomAccessFile。当咱们须要的时候再深刻研究也何尝不是一件好事。关于字符和字节,例如文本文件,XML这些都是用字符流来读取和写入。而如RAR,EXE文件,图片等非文本,则用字节流来读取和写入。面对如此复杂的类关系,有一个点是咱们必需要首先掌握的,那就是设计模式中的修饰模式,学会并理解修饰模式是搞懂流必备的前提条件哦。

字节流的学习

在具体的学习流以前,咱们必需要学的一个设计模式是装饰模式。由于从流的整个发展历史,出现的各类类之间的关系看,都是沿用了修饰模式,都是一个类的功能能够用来修饰其余类,而后组合成为一个比较复杂的流。好比说:

DataOutputStream out = new DataOutputStream(
                    new BufferedOutputStream(
                    new FileOutputStream(file)))
复制代码

从上面的代码块中你们不难看出这些类的关系:为了向文件中写入数据,首先须要建立一个FileOutputStream,而后为了提高访问的效率,因此将它发送给具有缓存功能的BufferedOutput-Stream,而为了实现与机器类型无关的java基本类型数据的输出,因此,咱们将缓存的流传递给了DataOutputStream。

从上面的关系,咱们能够看到,其根本目的都是为outputSteam添加额外的功能。而这种额外功能的添加就是采用了装饰模式来构建的代码。所以,学习流,必需要学好装饰模式。

下面的图是一个关于字节流的图谱,这张图谱比较全面的概况了咱们字节流中间的各个类以及他们之间的关系。


字节流的学习过程:

为何要按照一个学习路线来呢?缘由是他们的功能决定的。

OutputStream>FileOutputStream/FilterOutputStream>DataOutputStream->bufferedOutputStream

相应的学习InputStream方法就行了。

从学习的角度来,咱们应该先掌握FilterOutputStream, 以及FileOutputStream,这两个类是基本的类,从继承关系能够不难发现他们都是对 abstract 类 OutputStream的拓展,是它的子类。然而,伴随着 对 Stream流的功能的拓展,因此就出现了 DataOutputStream,(将java中的基础数据类型写入数据字节输出流中、保存在存储介质中、而后能够用DataOutputStream从存储介质中读取到程序中还原成java基础类型)。

这里多提一句FileOutputStream、DataOutputStream、FilterOutputStream三个类的关系的这种设计既使用了装饰器模式 避免了类的爆炸式增加。

为了提高Stream的执行效率,因此出现了bufferedOutputStream。bufferedOutputStream就是将本地添加了一个缓存的数组。在使用bufferedOutputStream以前每次从磁盘读入数据的时候都是须要访问多少byte数据就向磁盘中读多少个byte的数据,而出现bufferedOutputSteam以后,策略就改了,会先读取整个缓存空间相应大小的数据,这样就是从磁盘读取了一块比较大的数据,而后缓存起来,从而减小了对磁盘的访问的次数以达到提高性能的目的。

另一方面,咱们知道了outputStream(输出流)的发展历史后,咱们即可以知道如何使用outpuSteam了,一样的方法,咱们能够运用到inputStream中来,这样对称的解释就出现到了inputStream相关的中来了,因而,咱们对整个字节流就有了全方位的理解,因此这样子咱们就不会感受到流的复杂了。这个时候对于其余的一些字节流的使用(byteArrayOutputStream/PipeOutputStream/ObjectOutputStream)的学习就自须要在使用的时候看看API便可。

字符流的学习

下图则是一个关于字符流的图谱,这张图谱比较全面的概况了咱们字符流中间的各个类以及他们之间的关系。


字符流的学习和字节流的学习是同样的,它和字节流有着一样的发展过程,只是,字节流面向的是咱们未知或者即便知道了他们的编码格式也意义不大的文件(png,exe, zip)的时候是采用字节,而面对一些咱们知道文件构造咱们就可以搞懂它的意义的文件(json,xml)等文件的时候咱们仍是须要以字符的形式来读取,因此就出现了字符流。reader 和 Stream最大的区别我认为是它包含了一个readline()接口,这个接口标明了,一行数据的意义,这也是能够理解的,由于自有字符才具有行的概念,相反字节流中的行也就是一个字节符号。

字符流的学习历程:

Writer>FilterWriter>BufferedWriter>OutputStreamWriter>FileWriter>其余

同时类比着学习Reader相关的类。

FilterWriter/FilterReader

字符过滤输出流、与FilterOutputStream功能同样、只是简单重写了父类的方法、目的是为全部装饰类提供标准和基本的方法、要求子类必须实现核心方法、和拥有本身的特点。这里FilterWriter没有子类、可能其意义只是提供一个接口、留着之后的扩展。。。自己是一个抽象类。

BufferedWriter/BufferedReader

BufferedWriter是 Writer类的一个子类。他的功能是为传入的底层字符输出流提供缓存功能、一样当使用底层字符输出流向目的地中写入字符或者字符数组时、每写入一次就要打开一次到目的地的链接、这样频繁的访问不断效率底下、也有可能会对存储介质形成必定的破坏、好比当咱们向磁盘中不断的写入字节时、夸张一点、将一个很是大单位是G的字节数据写入到磁盘的指定文件中的、没写入一个字节就要打开一次到这个磁盘的通道、这个结果无疑是恐怖的、而当咱们使用BufferedWriter将底层字符输出流、好比FileReader包装一下以后、咱们能够在程序中先将要写入到文件中的字符写入到BufferedWriter的内置缓存空间中、而后当达到必定数量时、一次性写入FileReader流中、此时、FileReader就能够打开一次通道、将这个数据块写入到文件中、这样作虽然不可能达到一次访问就将全部数据写入磁盘中的效果、但也大大提升了效率和减小了磁盘的访问量!

OutputStreamWriter/InputStreamReader

输入字符转换流、是输入字节流转向输入字符流的桥梁、用于将输入字节流转换成输入字符流、经过指定的或者默认的编码将从底层读取的字节转换成字符返回到程序中、与OutputStreamWriter同样、本质也是使用其内部的一个类来完成全部工做:StreamDecoder、使用默认或者指定的编码将字节转换成字符;OutputStreamWriter/ InputStreamReader只是对StreamDecoder进行了封装、isr内部全部方法核心都是调用StreamDecoder来完成的、InputStreamReader只是对StreamDecoder进行了封装、使得咱们能够直接使用读取方法、而不用关心内部实现。

OutputStreamWriter、InputStreamReader分别为InputStream、OutputStream的低级输入输出流提供将字节转换成字符的桥梁、他们只是外边的一个门面、真正的核心:

OutputStreamWriter中的StreamEncoder:

一、使用指定的或者默认的编码集将字符转码为字节
二、调用StreamEncoder自身实现的写入方法将转码后的字节写入到底层字节输出流中。

InputStreamReader中的StreamDecoder:

一、使用指定的或者默认的编码集将字节解码为字符
二、调用StreamDecoder自身实现的读取方法将解码后的字符读取到程序中。

InputStreamReader中的StreamDecoder:

一、使用指定的或者默认的编码集将字节解码为字符
二、调用StreamDecoder自身实现的读取方法将解码后的字符读取到程序中。

在理解这两个流的时候要注意:java——io中只有将字节转换成字符的类、没有将字符转换成字节的类、缘由很简单——字符流的存在原本就像对字节流进行了装饰、加工处理以便更方便的去使用、在使用这两个流的时候要注意:因为这两个流要频繁的对读取或者写入的字节或者字符进行转码、解码和与底层流的源和目的地进行交互、因此使用的时候要使用BufferedWriter、BufferedReader进行包装、以达到最高效率、和保护存储介质。

FileReader/FileWriter

FileReader和FileWriter继承于InputStreamReader/OutputStreamWriter。

从源码能够发现FileWriter 文件字符输出流、主要用于将字符写入到指定的打开的文件中、其本质是经过传入的文件名、文件、或者文件描述符来建立FileOutputStream、而后使用OutputStreamWriter使用默认编码将FileOutputStream转换成Writer(这个Writer就是FileWriter)。若是使用这个类的话、最好使用BufferedWriter包装一下、高端大气上档次、低调奢华有内涵!

FileReader 文件字符输入流、用于将文件内容以字符形式读取出来、通常用于读取字符形式的文件内容、也能够读取字节形式、可是由于FileReader内部也是经过传入的参数构造InputStreamReader、而且只能使用默认编码、因此咱们没法控制编码问题、这样的话就很容易形成乱码。因此读取字节形式的文件仍是使用字节流来操做的好、一样在使用此流的时候用BufferedReader包装一下、就算冲着BufferedReader的readLine()方法去的也要使用这个包装类、不说他还能提升效率、保护存储介质。

字节流与字符流的关系

那么字节输入流和字符输入流之间的关系是怎样的呢?请看下图


一样的字节与字符输出流字节的关系也以下图所示


字节流与字符流的区别

字节流和字符流使用是很是类似的,那么除了操做代码的不一样以外,还有哪些不一样呢?

字节流在操做的时候自己是不会用到缓冲区(内存)的,是与文件自己直接操做的,而字符流在操做的时候是使用到缓冲区的字节流在操做文件时,即便不关闭资源(close方法),文件也能输出,可是若是字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,而且可使用flush方法强制进行刷新缓冲区,这时才能在不close的状况下输出内容

那开发中究竟用字节流好仍是用字符流好呢?

在全部的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会造成的,因此使用字节的操做是最多的。

若是要java程序实现一个拷贝功能,应该选用字节流进行操做(可能拷贝的是图片),而且采用边读边写的方式(节省内存)。

字节流与字符流的转换

虽然Java支持字节流和字符流,但有时须要在字节流和字符流二者之间转换。InputStreamReader和OutputStreamWriter,这两个为类是字节流和字符流之间相互转换的类。

InputSreamReader用于将一个字节流中的字节解码成字符:

有两个构造方法:

InputStreamReader(InputStream in);
复制代码
  • 功能:用默认字符集建立一个InputStreamReader对象
InputStreamReader(InputStream in,String CharsetName);
复制代码
  • 功能:接收已指定字符集名的字符串,并用该字符建立对象
OutputStream用于将写入的字符编码成字节后写入一个字节流。

一样有两个构造方法:

OutputStreamWriter(OutputStream out);
复制代码
  • 功能:用默认字符集建立一个OutputStreamWriter对象;
OutputStreamWriter(OutputStream out,String  CharSetName);
复制代码
  • 功能:接收已指定字符集名的字符串,并用该字符集建立OutputStreamWrite对象,为了不频繁的转换字节流和字符流,对以上两个类进行了封装。

BufferedWriter类封装了OutputStreamWriter类;
BufferedReader类封装了InputStreamReader类;

封装格式:

BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
  BufferedReader in= new BufferedReader(new InputStreamReader(System.in);
复制代码

利用下面的语句,能够从控制台读取一行字符串:

BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
  String line=in.readLine();复制代码

最后

后续还会更新更多精选文章,能够点赞关注下,欢迎你们在下面留言!

相关文章
相关标签/搜索