Java基础知识 IO &&字节流
1. IO
1、IO:input和output两个单词的缩写,input是输入,output是输出
站在内存的角度看待方向,从其他设备进入内存的,都是输入,从内存到其他设备的,都是输出
I/O就是用于设备之间进行数据交互的对象所属的类型
2、java中操作设备设备之间数据传输的对象,都是IO流对象,这些对象所属的类型,都在io包中。
2.IO分类
1、 分类的方式有两种:按照功能可以分类,按照流向也可以分类
2、 按照功能分类:
字节流:可以直接操作字节的流对象
字符流:可以直接操作字符的流对象
3、 按照流向分类:
输入流:其他设备流到内存的流对象
输出流:内存流到其他设备的流对象
4、 IO流的体系结构,根据分类,有四种流对象的类型
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流:
字符输入流:Reader
字符输出流:Writer
3.字节流
1、 可以直接操作字节信息的流对象
2、 根据流向,可以分成字节输入流和字节输出流
3、 顶层抽象父类分别是:InputStream 和 OutputStream
4、 根据交互设备的不同,有不同的具体子类
InputStream
1、 字节输入流的顶层抽象父类
2、 常用的方法
read():从当前的字节输入流中,获取一个字节
read(byte[] arr):将arr.length个字节,读取到arr中
3、 InputStream是一个抽象类,不能直接创建对象,只能由子类创建对象
FileInputStream
1、 InputStream的一个具体子类,用于和磁盘上的文件进行交互
2、 FileInputStream不仅可以一次读取一个字节,也可以一次读取很多个字节;不仅可以读取纯文本文件,也可以读取图片、视频、音频等非纯文本文件。
一切数据在计算机中都是以字节的形式在存储和计算
3、 构造方法:
FileInputStream(File f):将File对象封装成字节输入流,将来可以读取这个文件中的信息
FileInputStream(String path):将字符串封装成字节输入流,将来可以读取信息
注意事项:无论是哪个构造方法,都只能封装文件的路径,封装文件夹的路径没有任何意义,因为文件夹本身没有任何数据,所以也不能使用流对象读取数据。
4、 两个常用方法:
read():一次读取一个字节,返回值类型是int类型,原因要把返回的字节前面加上24个0,
无论读取到的是负数还是整数,都会变成正数,只要是从文件中读取到的数据,都是整数;
如果返回了-1,就说明不是从文件中读到的数据,而是整个方法专门准备的文件末尾的标记。
说明:虽然每次调用该方法,但是返回的内容却不会相同,因为文件指针在不断向后移动。
read(byte[] arr):一次读取多个字节,存储到字节数组中
OutputStream
1、 字节输出流的顶层抽象父类
2、 常用方法:
write(int b):将一个字节信息写出到指定的设备中
write(byte[] arr):将一个字节数组中的所有信息,写出到指定设备中
write(byte[] arr, int offset, int len):将一个字节数组arr中的从offset索引开始,总共len个字节写出到指定的设备中。
3、 这个类是抽象类,不能直接创建对象
FileOutputStream
1、 说明:可以将字节数据写出到指定的文件中
2、 构造方法:
FileOutputStream(File f):将f描述的路径文件封装成字节输出流对象
FileOutputStream(String path):将path描述的文件路径封装成字节输出流对象
FileOutputStream fos = new FileOutStream("1.txt",true);
如果没有1.txt 虚拟机会先自动地创建1.txt文件,true为决定是否追加,不写默认覆盖;
字节流有自动刷新的功能;
字符流不会自动刷新,需要手动刷新;
flush();
刷新完后需要手动的关闭;
close();
使用字节输出流写出数据,就进入到关联的文件中。
3、 注意事项:
1、成员方法都来自于抽象父类
2、写到磁盘上,存储的就是数字,既没有编码,也没有解码;如果使用文本编辑器读取这个文件,就会发生两步:先读取数据,通过编码表进行解码,让我们看到字符
4.文件拷贝
1、 含义:
将一个文件中的数据,拷贝到另一个文件中
2、 本质:
从一个文件中,使用输入流,读取一个字节
将这个字节,使用输出流,写出到另外一个文件中
5.文件拷贝效率提升
1、 使用一个字节一个字节拷贝的方式,效率非常低:IO的次数过多,有多少个字节,就要IO两倍的次数
2、 提升的思路:一次多读一些数据,一次多写出一些数据,使用FileInputStream中的read(byte[] arr)和FileOutputStream中的write(byte[] arr)。
数组的大小可以准备成和文件一样样大。配合InputStream中的available方法可以获取源文件的字节个数,用于创建数组的大小。
3、 数组属于内存的,内存的大小是有限的,如果文件过大,就无法创建大小相同的数组。
4、 只能考虑使用小一些的数组,每次拷贝源文件的一部分,多拷贝几次。涉及的方法:
InputStream中的read(byte[] arr):将数据读取到数组中,返回本次读到的有效字节的个数,
如果返回值为-1,表示本次读到的有效字节个数为0,表示到达了文件末尾
OutputStream中的write(byte[] arr):将数组中的所有数据,都写出到了目标文件中
OutputStream中的write(byte[] arr, int offset, int len):将数组中的指定部分的数据,
写出到目标文件中(在读取的时候,读到了多少有效字节,就将这么多有效字节写出到目标文件中),一般offset都选择0
5、 注意事项:最终拷贝方案就是小数组
1、数组的大小可以任意选择,数组越大,拷贝次数越少,拷贝的效率越高
2、一般情况,数组的大小使用1024的整数倍,在jdk中喜欢使用1024*8大小作为缓冲区的大小。
6.InputStream类型中read()方法和read(byte[] arr)方法的比较
1、 读取字节个数的区别:
read()方法一次只能读取一个字节
read(byte[] arr)方法一次可以读取多个字节,取决于数组的大小
2、 读取到的字节信息存储的位置不同:
read()读取到的文件字节信息是作为返回值进行返回的
read(byte[] arr)读取到的字节信息,存储到参数数组arr中
3、 返回值的含义不同:两个方法的返回值类型都是int类型
read()方法返回的是读取的有效字节本身
read(byte[] arr)方法的返回是读取到的有效字节的个数(返回值的取值范围:-1,1~arr.length)
7.高效缓冲流
1、 BufferedInputStream和BufferedOutputStream
2、 是包装类型:本身不具备读写的功能,只是在某个具体的流对象的基础上,对其进行加强,例如FileInputStream和FileOutputStream,原本效率较低,加强之后,就效率较高
3、 构造方法:
BufferedInputStream(InputStream is):将指定的具体的字节输入流传入构造方法的参数,形成一个高效版本的字节输入流
BufferedOutputStream(OutputStream os):将指定的具体的字节输出流传入构造方法的参数,形成一个高效版本的字节输出流
4、 使用:
这两个高效流还是字节流,还是InputStream和OutputStream,所以抽象父类中的那些方法仍然可以继续使用
5、 原理:
1、BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,
当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,
只返回了第一个字节给调用者。将来调用者再次调用read方法时,
当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,
由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,
就得让该对象到文件中读取下一个8192个字节了。
2、BufferedOutputStream高效的原理:在该类型中准备了一个数组,
存储字节信息,当外界调用write方法想写出一个字节的时候,
该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。
一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。
如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。
8.输出流中的close()和flush()的区别
1、 close方法会先调用flush方法
2、 close方法用于流对象的关闭,一旦调用了close方法,那么这个流对象就不能继续使用了
3、 flush只是将缓冲区中的数据,刷新到相应文件中,而不会将流对象关闭,可以继续使用这个流对象。但是如果flush方法使用过于频繁,那么丧失了缓冲区的作用。