IO流
1、概述
IO是Input与Output的缩写。
1)它具有以下特点:
1)IO流用来处理设备间的数据传输。
2)Java对数据的操作是通过流的方式。
3)Java用于操作流的对象都在IO包中。
4)流分类:按操作数据分为:字节流和字符流;
按流向分为:输入流和输出流。
5)流只能操作数据,不能操作文件。
2)字符流与字节流区别:
主要区别是他们的处理方式,字节流是最基本的,采用ASCII编码,所有的InputStream和OutputStream的子类都是,主要用在二进制数据处理,而字符流采用Unicode编码,按虚拟机encode来处理,要进行字符集的转化。
类名称 字节流 字符流
输入 InputStream Reader
输出 OutputStream Writer
这四个类派生出来的子类名称都是以其父类名作为子类名的后缀,如InputStream子类FileInputStream;Reader子类FileReader。
2、字符流
1)概述
字符流中的对象融合了编码表,使用的是当前系统的默认编码,它只用于处理文字数据,而字节流可以处理媒体(图片,音乐)数据。
最常见的数据体现形式是文件,专门有一个用于操作文件的Writer子类对象FileWriter,该流对象一被初始化就必须有文件存在。
2)字符流
A、写入字符流使用方法:
a、创建一个FileWriter对象,明确数据要存放的目的地。初始化就必须要明确被操作的文件,若该目录下已有同名文件,则将被覆盖。
如:FileWriterfw = new FileWriter("D:\\Demo.txt")。
b、调用write(String s)方法,将字符串写入到流中。
如:fw.write("abcd")。
c、调用flush()方法,刷新对象中的缓冲数据,将数据刷新到目的地。
如:fw.flush()。
d、调用close()方法关闭流资源。关闭前会刷新一次将数据刷新到目的地中。如:fw. close()。
B、读取字符流使用方法:
a、创建一个文件读取流FileReader对象,和指定名称的文件相关联,要保证该文件已经存在,否则将会发生FileNotFoundException异常。
b、调用读取流对象的read()方法。read()方法是一次读一个字符,会自动往下读直到末尾。读取有两种方式:读取单个字符和通过字符数组进行读取
c、调用close()方法将资源关闭。
注意:windows系统中的文件内换行用\r\n两个转义字符表示,linux系统只用\n表示换行。
3)close()和flush()的区别:
flush()刷新后,流可以继续使用,而close()刷新后,将会关闭流,不可再写入字符流。
4)为什么要关闭资源呢?
因为java写入数据是调用系统内部方式完成的,所以使用完系统资源后一定要关闭资源。
5)续写文件的数据
通过构造函数FileWriter(Strings, boolean append)来完成,在创建对象时,传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
6)异常
由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中需要对IOException异常进行try处理。
3、字符流缓冲区
对应的类:BufferedReader和BufferedWriter。
缓冲区的出现,提高了流对数据的读写效率,其要结合流才可以使用,所以在缓冲区创建前要先创建流对象,将流对象初始化到缓冲区构造函数中。
缓冲技术的原理:对象中封装了数组,将数据存入后,一次性取出。
1)写入流缓冲区(BufferedWriter)的使用步骤:
A、创建一个字符写入流对象。
如:FileWriterfw = newFileWriter("D:\\Demo.txt")。
B、加入缓冲,将需要被提高效率的流对象作为参数传递给缓冲区的构造函数。如:BufferedWriter bufw = new BufferedWriter(fw)。
C、调用write()方法写入数据到指定文件。如:bufw.write("adcd")。
D、调用flush()方法刷新缓冲区。只要用到缓冲区,就一定要刷新,虽然关闭流同样会刷新,但是为了不出意外并保证数据存在,建议写入一次就刷新一次。如:bufw.flush()。
E、关闭缓冲区。其实就是关闭缓冲区中的流对象。如:bufw.close()。
BufferedWriter缓冲区提供了一个跨平台的换行符newLine(),其可以在不同操作系统上调用,用作数据换行。
2)读取流缓冲区(BufferedReader)的使用步骤:
A、创建一个读取流对象和文件相关联。
如:FileReaderfr = new FileReader("D:\\Demo.txt ")。
B、建立缓冲区,将字符读取流对象作为参数传递给缓冲区对象的构造函数。
如:BufferedReader bufr = newBufferedReader(fr)。
C、调用缓冲区提供的readLine()方法读取,如果到达文件末尾,则返回null。
如:String s = bufr.readLine()。
D、关闭流资源。如:bufr.close()。
3)BufferedReader缓冲区提供了一个一次读一行的方法readLine(),其方便于对文本数据的读取,当返回null时就表示读到了文件末尾,其只返回回车符之前的数据内容,不返回回车符。该方法的原理还是read()方法一次读一个的方法,因为无论读一行还是读取多个字符,其实都是在硬盘上一个一个读取。
4)BufferedReader中有个直接的子类:LineNumberReader类,其是个装饰类,其特有的方法:
setLineNumber():设置初始行号;
getLineNumber():获取当前行号。
4、装饰设计模式
对已有的对象进行功能增强,可定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该个类被称为装饰类。
1)装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰对象的功能,提供更强的功能。
2)装饰和继承的区别:
装饰模式比继承要灵活,避免了继承体系的臃肿,并降低了类与类之间的关系,从继承结构转为组合结构。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,灵活性较强,所以装饰类和被装饰的类通常都是属于一个体系。
5、字节流
1)概述:
字节流和字符流的基本操作是相同的,但字节流可以操作媒体文件,由于媒体文件数据中都是以字节存储的,所以字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
字节输入流:InputStream
字节输出流:OutputStream
2)InputStream特有方法:
int available():返回文件中的字节个数。
可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断,但是如果文件较大,而虚拟机启动分配的默认内存一般为64M,此时数组长度所占内存空间就会溢出,所以当文件不大时可以使用。
3)字节流缓冲区:同样是提高了字节流的读写效率。
read():将字节byte型提升为int型,为了避免-1的发生。
write():将int型强转为byte型。
字节流缓冲区原理就是将数据拷贝一部分,读取一部分,循环直到数据全部读取完毕。
具体过程如下:
A、先从数据中读取固定数组长度的字节,存入定义的数组中,然后通过read()方法读取数组中的元素存入缓冲区;
B、循环A这个动作,直到取出最后一组数据存入数组;数组可能未填满,但同样也取出包含的元素。每次取出的时候,都有一个指针在移动,读到数组结尾就自动回到数组头部。
C、当文件中的数据全部都被读取后,read()方法就返回-1。
4)注意事项:
为什么字节流的read()方法返回值类型是int而不是byte?
因为我们判断读取结束是通过结尾标记-1来确定的,读取过程中就有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1,那么就会数据还没有读完,就出现结束的情况,所以将读到的字节进行int类型的提升(在保留原字节数据的前面了补24个0)。
在写入数据时,只写该int类型数据的最低8位。
6、键盘录入
标准输入设备:System.in,键盘;InputStream;
标准输出设备:System.out:控制台;PrintStream;
PrintStream是FilterOutputStream的子类。
1)整行录入
使用输入流进行键盘录入时,只能一个字节一个字节进行录入,为了提高效率,可以自定义一个数组将一行字节进行存储,当一行录入完毕,再将一行数据进行显示,这种录入方式和字符流读一行数据的原理是一样的,相当readLine方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中的方法,而键盘录入的read方法是字节流InputStream的方法,那么就需要将字节流转成字符流再使用字符流缓冲区的readLine方法,这就需要用到转换流。
2)转化流
转换流是字符流与字节流之间的桥梁,方便于字符流与字节流之间的操作,当字节流中的数据都是字符时,转成字符流操作更有效率。
A、字节流转成字符流(读取转换流)
InputStreamReader()
为了提高效率,加入缓冲区BufferedReader进行键盘录入:
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
B、字符流转成字节流(输出转换流)
OutputStreamWriter()
也加入缓冲区BufferedWriter进行输出:
BufferedWriter bufw =
new BufferedWriter(newOutputStreamWriter(System.out));
7、流操作的基本规律
1)通过三个明确来完成使用哪个流对象:
A、明确源和目的:
源:输入流,InputStream和Reader;
目的:输出流,OutputStream和Writer。
B、操作的数据是否是纯文本:
是:字符流;
否:字节流。
C、明确体系后,再通过设备来明确使用哪个具体的对象:
源设备:内存,硬盘,键盘;
目的设备:内存,硬盘,控制台。
2)将一个文本文件中的数据存储到另一个文件中(复制文件):
A、源还是目的:是源,使用读取流:InputStream和Reader;
明确体系:是操作文本,Reader;
明确设备:硬盘上的一个文件,用Reader体系中可以操作文件的对象FileReader;
是否需要提高效率:需要,加入Reader体系中缓冲区BufferedReader;
FileReaderfr = new FileReader("D:\\a.txt");
BufferedReader bufr = newBufferedReader(fr);
B、源还是目的:输出流:OutputStream和Writer;
明确体系:是操作文本,Writer;
明确设备:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:需要,加入Writer体系中缓冲区BufferedWriter;
FileWriter fw = new FileWriter("D:\\b.txt");
BufferedWriter bufw = newBufferedWriter(fw);
3)将键盘录入的数据保存到一个文件中:
A、源:InputStream和Reader;
明确体系,是不是纯文本:是,Reader;
明确设备:键盘,System.in,为了操作键盘的文本数据方便,转成字符流按照字符串操作,所以就明确了Reader,然后用转换流InputStreamReader将System.in转成Reader。
InputStreamReader ir = new InputStreamReader(System.in);
是否需要提高效率:需要
BufferedReaderbufr = new BufferedReader(ir);
B、目的:OutputStream Writer;
明确体系,是否是存文本:是,Writer;
明确设备:硬盘的一个文件,使用FileWriter;
FileWriter fw = newFileWriter("c.txt");
是否需要提高效率:需要
BufferedWriter bufw =new BufferedWriter(fw);
4)把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。
目的:OutputStream和Writer;
是否是存文本:是,Writer;
明确设备:硬盘上的一个文件,使用FileWriter;但是默认编码表为GBK,而要求指定编码表UTF-8,而指定编码表只有转换流可以,所以要使用对象的是OutputStreamWriter转换流。该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream;
OutputStreamWriterosw =
new OutputStreamWriter(new FileOutputStream("D:\\d.txt"),"UTF-8");
明确是否需要高效:需要
BufferedWriter bufw = newBufferedWriter(osw);
5)转换流什么时候使用:
转换流是字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。
8、标准输入输出设备改变
通过System类的setIn和setOut方法可以对默认设备进行改变:
比如:System.setIn(new FileInputStream(“1.txt”)):将源改成文件1.txt;
System.setOut(new PrintStream(“2.txt”)):将目的改成文件2.txt。