8.0、Java_IO流 - 如何利用缓冲区提高读写效率 ?
简单介绍:
FileInputStream 通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等);Java 也提供了 FileReader 字符流 专门读取文本文件;
FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件;Java 也提供了 FileWriter 字符流 专门写入文本文件;
我们先用案列来了解一下,FileInputStream 和 FileOutputStream ->
事先准备好一张图片 - 阿尼亚.jpg ->
案例 :创建 FileDemo2.java 文件,如下所示 ->
public class FlieDemo2 {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream file = null;
FileOutputStream file2 = null;
try {
//获取 D 盘下的 阿尼亚.jpg 文件
file = new FileInputStream("D:/阿尼亚.jpg");
//获取 D 盘下的 pic.jpg 文件
file2 = new FileOutputStream("D:/pic.jpg");
int tmp = 0;
while((tmp = file.read()) != -1) {
//一个字节一个字节读取 阿尼亚.jpg 文件
System.out.println(tmp);
//将每一个读取出来的字节数据再写到pic.jpg 文件中去
file2.write(tmp);
}
}catch(Exception e) {
e.printStackTrace();
//关闭流
}finally {
try {
if(file != null) {
file.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
执行main方法后输出结果如下所示 ->
pic.jpg打开后如下所示 ->
说明我们成功将 阿尼亚.jpg 文件读取,且将读取出来的内容写入到了 pic.jpg 文件中;
数据缓冲区:
看完这个案例之后明显的会感觉到有一个问题,那就是文件读写的效率太慢了,读一个写一个;
打个比方 ->
我们在超市买了 100 斤大米,然后需要把这 100 斤大米扛回家,一次性肯定拿不动 100 斤,所以按照 案例 的方式就相当于我们一次拿一粒米回家,到家后每次从手中放下一粒米,再回到超市去继续拿......这样要拿放 N 次才能把这 100 斤拿完回家,显而易见效率低的不行......
我给大家提供两种解决方案,如下所示:
【解决方案 1】
我们可以做一个一次能装 20 斤大米的包,然后每次就能拿 20 斤大米,到家后每次从手中放下 20 斤大米,把包再拿回超市继续拿,拿五次就能完成;
注意 ->
这里的包其实就相当于是创建了一个 数据缓冲区 [ 代码:byte[ ] buff = new byte(1024);缓冲区的大小必须为 2 的次幂 ] ;他能解决我们数据读写效率低下的问题;
用代码稍作解释 ->
FileInputStream fis = new FileInputStream("D:/rice.txt");
//创建缓冲区,大小为 1024 byte
byte[ ] buff = new byte[1024];
//一次拿一粒大米
fis.read();
//一次拿 20 斤大米
fis.read(buff);
FileOutputStream fos = new FileOutputStream("D:/home.txt");
//创建一个大小为 1024 的缓冲区,当然缓冲区只创建一次即可,可以复用
byte[ ] buff = new byte[1024];
//一次从手中放下一粒大米
fos.write();
//一次从从手中方法 20 斤大米
fos.write ( buff,起始位置,结束位置 );
解决方案 1 ,代码如下所示 ->
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileDemo3 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:/阿尼亚.jpg");
fos = new FileOutputStream("D:/pic.jpg");
byte[] buff = new byte[1024];
int tmp = 0;
//每次读取 1024 个字节的数据,将其放到缓冲区 buff 中
while((tmp = fis.read(buff)) != -1) {
//输出流,每次将数组 buff 中,从位置 0 开始一直到上面read()读取到的放在数组中的最后一个字节位置写,将其全部入文件 pic.jpg
fos.write(buff,0,tmp);
}
} catch (Exception e) {
e.printStackTrace();
//关闭流
}finally {
try {
if(fos != null){
//关闭输出流
fos.close();
}
if(fis != null){
//关闭输出流
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上述代码中:
fos.write(buff,0,tmp);用文件输出流对象调用 write(byte[ ],int,int);方法,[ 解释:每次将数组 buff 中,从位置 0 开始一直到上面 read( ) 读取到的放在数组中的最后一个字节位置写,将其全部入文件 pic.jpg ]
【解决方案 2】
还是上面的案例,假设我们的力气超级大,一次性就能把 100 斤大米扛回去,此时我们就不是借助 20 斤的袋子了,而是创建一个能装 100 斤大米的袋子,把大米放进去,直接一次性扛回家就OK了;
那么说白了方式二也是借助 缓冲区 来提高读写效率,只不过 缓冲区大小 不是自定义了,而是通过 " 文件输入流对象.available() " 方法去定义,该方法会返回当前输入流对象文件的大小预估值;
//创建文件输入流、文件输出流对象
FileInputStream fis = new FileInputStream("D:/阿尼亚.jpg");
FileOutputStream fos = new FileOutputStream(D:/pic.jpg);
//创建缓冲区 buff,缓冲区大小为 fis.available() ,该方法会预估 file.txt 文件大小
byte[ ] buff = new byte[ fis.available( ) ];
//读取文件内容,放到 buff 缓冲区中
fis.read( buff );
//将缓冲区 buff 中的数据写出到 pic.jpg 中
fos.write(buff);
方案2 的代码与 方案1 代码基本相同,唯一不同之处 -> 就在于缓冲区的大小由 available() 方法来定义;所以这里就不做展示 方案2 代码了;
【 到这里要提醒一下大家 】
在文件很大很大的情况,就不建议大家用 【解决方案 2】这种方式了,因为虽然省事一次性就搞定了,但是却十分的浪费内存空间资源;
此时就应该使用 【解决方案 1】,虽然每次要搬多次,但不至于将内存空间资源都占了,最后可能导致程序崩溃;
总结一句话就是:两种方案都是在用 空间 换取 效率;方案1 用较少的空间换取较低的效率,方案2 用较多的空间换取较高的效率;[ 当然,无论是 方案1 还是 方案2 都比不用缓冲区的效率要高得多得多 ];