IO流的分类
|- 字节流
|- 字节输入流 InputStream 抽象类
|- FileInputStream 操作文件的字节输入流
|- 字节输出流 OuputStream抽象类
|- FileOutputStream 操作文件的字节输出流
|- 字符流
|- 字符输入流 Reader抽象类
|- InputStreamReader 输入操作的转换流
|- FileReader 用来操作文件的字符输入流(简便的流)
|- 字符输出流 Writer抽象类
|- OutputStreamWriter 输出操作的转换流
|- FileWriter 用来操作文件的字符输出流(简便的流)
OutputStream此抽象类,是表示输出字节流的所有类的超类。操作的数据都是字节,定义了输出字节流的基本共性功能方法。输出流中定义都是写write方法,如下图:
OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。
FileOutputStream类,即文件输出流,是用于将数据写入File的输出流。从名字可以看出,前一半是功能(File),后一半是父类名称(OutputStream)。
我们直接new FileOutputStream(file)这样创建对象,写入数据,会覆盖原有的文件,那么我们想在原有的文件中续写内容怎么办呢?
继续查阅FileOutputStream的API。发现在FileOutputStream的构造函数中,可以接受一个boolean类型的值,如果值true,就会在文件末位继续添加: FileOutputStream fos = newFileOutputStream( file, true );
在前面编写代码中都发生了IO的异常。我们在实际开发中,对异常时如何处理的,我们来演示一下。
注意几点:
1.保证流对象变量的作用域足够,即在try外面声明变量,在try里面建立流对象
2.Catch里面怎么处理异常:
a)先输出异常信息,目的:看到哪里出了问题
b)抛运行时异常,停下程序,重新尝试
3.如果流对象创建失败,需要关闭资源吗?
a)在new对象的时候,如果失败了,就没有占用系统资源,也就不需要关闭
b)所以在close之前,对流对象进行判空操作。当变量不是null时对象建立成功,此时才需要关闭流对象
注意:编译期异常,必须处理,不然无法编译通过
运行期异常,程序运行过程中,产生的异常信息
通过前面的学习,我们可以把内存中的数据写出到文件中,那如何想把内存中的数据读到内存中,我们通过InputStream可以实现。InputStream此抽象类,是表示字节输入流的所有类的超类,定义了字节输入流的基本共性功能方法。
int read():读取一个字节并返回,没有字节返回-1.
int read(byte[] b): 读取一定量的字节数,并存储到字节数组b中,返回读取到的字节数。
InputStream有很多子类,其中子类FileInputStream可用来读取文件内容。
FileInputStream 从文件系统中的某个文件中获得输入字节。构造方法如下
在读取文件中的数据时,调用read()方法,实现从文件中读取数据
在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,于是我们可以定义数组作为临时的存储容器,这时可以调用重载的read(byte[] b)方法,一次可以读取多个字符。
原理:读取一个已有的数据,并将这些读到的数据写入到另一个文件中。
经过前面的学习,我们基本掌握的文件的读写操作,在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时怎么办呢?
用字符流!!只能对文本文件进行读写!
我们知道计算机底层数据存储的都是二进制数据,而我们生活中的各种各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?这时美国人他们就把每一个字符和一个整数对应起来,就形成了一张编码表,美国人他们的编码表就是ASCII表。其中就是各种英文字符对应的编码。
编码表:其实就是生活中字符和计算机二进制的对应关系表。
1、ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx
2、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
3、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
GB18030:最新的中文码表,目前还没有正式使用。
4、unicode:国际标准码表:无论是什么文字,都用两个字节存储。
Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
Java中的字符串是按照系统默认码表来解析的。简体中文版字符串默认的码表是GBK。
5、UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言;常见的编码 GBK UTF-8 ISO-8859-1
文字--->(数字) :编码。“abc”.getBytes() byte[]
(数字)--->文字 : 解码。 byte[] b={97,98,99} newString(b)
上述程序中我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API中是否给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。
read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。
查阅FileInputStream的API,发现FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
打开FileReader的API介绍。用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。构造方法如下
Writer是写入字符流的抽象类。其中描述了相应的写的动作。具体方法有
查阅FileOutputStream的API,发现FileOutputStream 用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用 FileWriter。
打开FileWriter的API介绍。用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。构造方法如下
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
思路:1,既然是文本涉及编码表。需要用字符流。
2,操作的是文件。涉及硬盘。
3,有指定码表吗?没有,默认就行。
操作的是文件,使用的默认码表。直接使用字符流操作文件的便捷类。FileReader FileWriter
查阅OutputStreamWriter的API介绍,OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的字符编码表,将要写入流中的字符编码成字节。它的作用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
查阅InputStreamReader的API介绍,InputStreamReader 是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
注意:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象。
发现有如下继承关系:
父OutputStreamWriter:
|------------子FileWriter:
父InputStreamReader:
|------------子FileReader;
父类和子类的功能有什么区别呢?
OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(newFileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr =new FileReader("a.txt");
上面这三句代码的功能是一样的,其中第三句最为便捷。
注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。什么时候用子类呢?条件:1、操作的是文件。2、使用默认编码。
总结:字节--->字符 : 看不懂的--->看的懂的。 需要读。输入流。 InputStreamReader
字符--->字节 : 看的懂的--->看不懂的。 需要写。输出流。 OutputStreamWriter
字节缓冲流根据流的方向,共有2个
写入数据到流中,字节缓冲输出流 BufferedOutputStream
读取流中的数据,字节缓冲输入流 BufferedInputStream
它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了IO流的读写速度通过字节缓冲流,进行文件的读写操作,写数据到文件的操作
构造方法:public BufferedOutputStream(OutputStream out),需要创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
刚刚我们学习了输出流实现了向文件中写数据的操作,那么,现在我们完成读取文件中数据的操作
构造方法:public BufferedInputStream(InputStream in)
可以完成文本数据的高效的写入与读取的操作
字符缓冲输出流BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。新方法:void newLine() 根据当前的系统,写入一个换行符
字符缓冲输入流 BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。新方法:public String readLine()读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
IO流中对象很多,解决问题(处理设备上的数据时)到底该用哪个对象呢?
把IO流进行了规律的总结(四个明确):
明确一:要操作的数据是数据源还是数据目的。先根据需求明确要读,还是要写。
源:InputStream Reader
目的:OutputStream Writer
明确二:要操作的数据是字节还是文本呢?已经明确到了具体的体系上。 源:字节:InputStream
文本文件(看得懂的):Reader
目的:字节:OutputStream
文本文件(看得懂的):Writer
明确三:明确数据所在的具体设备。完全可以明确具体要使用哪个流对象。源设备:硬盘:文件 File开头。
内存(一般用不上):数组,字符串。
键盘:System.in;
网络:Socket 字节流
目的设备:
硬盘:文件 File开头。
内存:数组,字符串。
屏幕:System.out
网络:Socket 字节流
明确四:是否需要额外功能呢?额外功能:
转换吗?转换流。InputStreamReader OutputStreamWriter
高效吗?缓冲区对象。BufferedXXX
流可分为:
第一类:InputStream(字节输入流)
子类FileInputStream 操作文件的字节输入流
子类BufferedInputStream 高效的字节输入流
第二类:OuputStream(字节输出流)
子类FileOutputStream 操作文件的字节输出流
子类BufferedOuputStream 高效的字节输出流
第三类:Reader(字符输入流)
子类InputStreamReader 输入操作的转换流(把字节流封装成字符流)
孙子类FileReader 操作文件的字符输入流
子类BufferedReader 高效的字符输入流
第四类: Writer(字符输出流)子类OutputStreamWriter 输出操作的转换流(把字符流封装成字节流)
孙子类FileWriter 操作文件的字符输出流
子类BufferedWriter 高效的字符输出流