概念
-
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
-
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
BufferedInputStream
和 和BufferedOutputStream
BufferedReader
和 和BufferedWriter
原理
- 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
- 当使用
BufferedInputStream
读取字节文件时,BufferedInputStream
会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件读取下一个8192个字节数组。 - 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream
才会把缓冲区中的数据一次性写到文件里。使用方法flush()
可以强制将缓冲区的内容全部写入输出流 - 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
flush()
方法的使用:手动将buffer中内容写入文件- 如果是带缓冲区的流对象的
close()
方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出
示例
/**
* 缓冲流的使用(处理流的一种)
* 1.缓冲流
* BufferedInputStream
* BufferedOutputStream
* BufferedReader
* BufferedWriter
*
* 2.作用:提供流的读取、写入的速度
* 提高读写速度的原因:内部提供了一个缓冲区
*
* 3.处理流,就是“套接”在已有的流的基础上。
*
* @author zlg
* @create 2019-10-08 23:58
*/
public class BufferedStreamTest {
/**
* 实现非文本文件的复制
*/
@Test
public void copyFileTest(){
String srcPath = "D:\\BaiduNetdiskDownload\\videotest.avi";
String destPath = "D:\\BaiduNetdiskDownload\\videotest_dest2.avi";
long start = System.currentTimeMillis();
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制完成!耗时:"+(end-start)+"ms!"); // 792ms 1850ms
}
//封装文件复制的方法
public void copyFile(String srcPath,String destPath){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.file实例化
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.stream实例化
//2.1filestream实例化
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//2.2bufferedstream实例化
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.data处理
byte[] buffer = new byte[1024];
int len;
while ( (len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
// bos.flush();//刷新缓冲区
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.stream关闭
//要求:先关闭外层的流,再关闭内层的流
if (bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
// fos.close();
// fis.close();
}
}
/**
* 使用BufferedReader和BufferedWriter实现文本文件的复制
*/
@Test
public void bufferedCharStreamTest(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
//stream实例化
br = new BufferedReader(new FileReader("dbcp.txt"));
bw = new BufferedWriter(new FileWriter("dbcp_dest.txt"));
//data处理
//方式一:
// char[] cbuf = new char[1024];
// int len;
// while ( (len = br.read(cbuf)) != -1){
// bw.write(cbuf,0,len);
// }
//方式二:
String data;
while ( (data = br.readLine()) != null) {
bw.write(data); //data中不包含换行符
bw.newLine(); //提供换行的操作
// bw.write(data + "\n");//data中不包含换行符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//stream关闭
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
应用
图片的加密与解密
public class PicTest {
//图片的加密
@Test
public void test1() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("爱情与友情.jpg");
fos = new FileOutputStream("爱情与友情secret.jpg");
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
//字节数组进行修改
//错误的
// for(byte b : buffer){
// b = (byte) (b ^ 5);
// }
//正确的
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^ 5);
}
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//图片的解密
@Test
public void test2() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("爱情与友情secret.jpg");
fos = new FileOutputStream("爱情与友情4.jpg");
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
//字节数组进行修改
//错误的
// for(byte b : buffer){
// b = (byte) (b ^ 5);
// }
//正确的
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^ 5);
}
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
获取文本上字符出现的次数,把数据写入文件
/**
* 获取文本上字符出现的次数,把数据写入文件
*
* 思路:
* 1.遍历文本每一个字符
* 2.字符出现的次数存在Map中
*
* Map<Character,Integer> map = new HashMap<Character,Integer>();
* map.put('a',18);
* map.put('你',2);
*
* 3.把map中的数据写入文件
*/
public class WordCount {
/*
说明:如果使用单元测试,文件相对路径为当前module
如果使用main()测试,文件相对路径为当前工程
*/
@Test
public void testWordCount() {
FileReader fr = null;
BufferedWriter bw = null;
try {
//1.创建Map集合
Map<Character, Integer> map = new HashMap<Character, Integer>();
//2.遍历每一个字符,每一个字符出现的次数放到map中
fr = new FileReader("dbcp.txt");
int c = 0;
while ((c = fr.read()) != -1) {
//int 还原 char
char ch = (char) c;
// 判断char是否在map中第一次出现
if (map.get(ch) == null) {
map.put(ch, 1);
} else {
map.put(ch, map.get(ch) + 1);
}
}
//3.把map中数据存在文件count.txt
//3.1 创建Writer
bw = new BufferedWriter(new FileWriter("wordcount.txt"));
//3.2 遍历map,再写入数据
Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
for (Map.Entry<Character, Integer> entry : entrySet) {
switch (entry.getKey()) {
case ' ':
bw.write("空格=" + entry.getValue());
break;
case '\t'://\t表示tab 键字符
bw.write("tab键=" + entry.getValue());
break;
case '\r'://
bw.write("回车=" + entry.getValue());
break;
case '\n'://
bw.write("换行=" + entry.getValue());
break;
default:
bw.write(entry.getKey() + "=" + entry.getValue());
break;
}
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关流
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}