一、缓冲流概述
1.1 包装流
IO流划分:
节点流: 可以从或向一个特定的地方(节点)读写数据(例如:FileOutputStream、FileInputStream、FileWriter和FileReader等)。
包装流:对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写(例如:缓冲流、转换流和合并流等)。
1.2 缓冲流
缓冲流是一个包装流,目的是缓存作用,加快读取和写入数据的速度。
字节缓冲流:BufferedInputStream、BufferedOutputStream
字符缓冲流:BufferedReader、BufferedWriter
注意:缓冲流属于包装流,只能对已有的流进行封装,不能直接关联文件进行操作!
1.3 缓冲流原理
原理:先把数据先写入缓冲区,等缓冲区满了,再把数据写到文件里。
在JVM中会开辟一块缓冲区的内存空间,然后将文件中的数据读取到缓冲区中,直到读满这个缓冲,才会将缓冲区中的数据获取到程序中。
总结:缓冲流直接内存中读取数据的方式要比每次都访问磁盘的效率高很多,所以缓冲流的读取效率高。
二、字节缓冲流
字节缓冲流根据流的方向,共有 2 个:
- 写入数据到流中,字节缓冲输出流 BufferedOutputStream
- 读取流中的数据,字节缓冲输入流 BufferedInputStream
它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了 IO 流的读写速度。
2.1 BufferedOutputStream类
BufferedOutputStream类从FilterOutputStream类继承而来,FilterOutputStream类继承于OutputStream类。
构造方法:
从构造方法中我们可以知道BufferedOutputStream没有无参构造方法,它必须传入一个OutputStream (一般是FileOutputStream)来一起使用。另外,我们还可以指定缓冲区的大小,一般情况下,使用默认的缓冲区大小就足够了(默认大小为8192)。
常用的方法:
字节输出缓冲流(BufferedOutputStream)没有声明字节任何新的方法,它只是覆盖了基类的方法。
关于flush()方法使用场景:
假设一个文件有1122个字节,缓冲区大小为100个字节,那么要读取12次,最后一次只有22个字节,但此时缓冲区并没有存满,不能将存储的内容从缓冲区中取出。这时我们需要调用缓冲流自带的一个方法flush(强制写入)将最后一次读取的内容强制写入到文件中去。
向文件中写出数据
【示例】使用字节缓冲流向文件中写数据
public class BufferedByteDemo { public static void main(String[] args) throws IOException { // 指定目的地 FileOutputStream fos = new FileOutputStream("test.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); // 存数据 bos.write("whpowernode".getBytes()); // 刷新数据 bos.flush(); // 关闭流 bos.close(); } } |
关闭缓冲流时:
1、关闭缓冲流的时候,默认先调用flush()方法,然后再去做关闭流的操作。
2、关闭缓冲流的方法时,方法内部已经实现对字节流的关闭,所以此处我们只需要关闭缓冲流即可。
2.2 BufferedInputStream类
BufferedInputStream类从FilterInputStream类继承而来,FilterInputStream类继承于InputStream类。
构造方法
从构造方法中我们可以知道BufferedInputStream没有无参构造方法,它必须传入一个InputStream (一般是FileInputStream)来一起使用。另外,我们还可以指定缓冲区的大小,一般情况下,使用默认的缓冲区大小就足够了(默认大小为8192)。
常用的方法
字节输入缓冲流(BufferedInputStream)没有声明字节任何新的方法,它只是覆盖了基类的方法。
从文件中读入数据
【示例】使用字节缓冲流从文件中读数据
public class BufferedByteDemo { public static void main(String[] args) throws IOException { // 指定目的地 FileInputStream fis = new FileInputStream("test.txt"); BufferedInputStream bis = new BufferedInputStream(fis); int len = 0; byte[] by = new byte[1024]; StringBuilder sb = new StringBuilder(); // 取数据 while((len = bis.read(by)) != -1) { sb.append(new String(by, 0, len)); } System.out.println(sb); // 关闭流 bis.close(); } } |
我们查看API底层源码,可知:调用关闭缓冲流的方法时,方法内部已经实现对字节流的关闭,所以此处我们只需要关闭缓冲流即可。
2.3 字节缓冲流拷贝案例
【示例】字节缓冲流拷贝案例
public class BufferedByteDemo { public static void main(String[] args) throws IOException { // 指定数据源 FileInputStream fis = new FileInputStream("E:\\资料\\战狼2.avi"); BufferedInputStream bis = new BufferedInputStream(fis); // 指定目的地 FileOutputStream fos = new FileOutputStream("E:\\ 战狼2.avi"); BufferedOutputStream bos = new BufferedOutputStream(fos); // 读数据 byte[] by = new byte[1024]; int len = 0; while((len = bis.read(by)) != -1) { // 存数据 bos.write(by, 0, len); // 刷新数据 bos.flush(); } // 关闭流 bis.close(); bos.close(); } } |
三、字符缓冲流
3.1 BufferedWriter类
BufferedWriter类从Writer类继承而来。
构造方法
缓冲区的默认大小为8192,一般情况下,使用默认的缓冲区大小就足够了,当然我们还可以指定缓冲区的大小。
常用的方法
字符输出缓冲流(BufferedWriter)不但覆盖了父类的方法,还新增了newLine()方法。
newLine()方法会根据当前的操作系统,写入一个换行符(不同操作系统换行符不一样,例如:windows操作系统的换行符为“\r\n”,Linux操作系统的换行符为“\n”)。
关于flush()方法使用场景:
假设一个文件有1122个字符,缓冲区大小为100个字符,那么要读取12次,最后一次只有22个字符,但此时缓冲区并没有存满,不能将存储的内容从缓冲区中取出。这时我们需要调用缓冲流自带的一个方法flush(强制写入)将最后一次读取的内容强制写入到文件中去。
向文件中写数据
【示例】使用字符缓冲流向文件中写数据
public class BufferedCharDemo { public static void main(String[] args) throws IOException { // 创建基本字符输出流 Writer w = new FileWriter("demo.txt"); // 把基本的流进行包装 BufferedWriter bw = new BufferedWriter(w); // 写数据 for(int i = 0; i < 5; i++) { bw.write("你好啊"); // 添加换行符 bw.newLine(); bw.flush(); } // 关闭流 bw.close(); } } |
注意:关闭缓冲流时已经实现对字符流的关闭,所以此处我们只需要关闭缓冲流即可。
3.2 BufferedReader类
BufferedReader类从Reader类继承而来。
1)构造方法
缓冲区的默认大小为8192,一般情况下,使用默认的缓冲区大小就足够了,当然我们还可以指定缓冲区的大小。
2)常用的方法
字符输入缓冲流(BufferedReader)不但覆盖了父类的方法,还新增了readLine()方法。
String readLine()方法从输入流中读取一行文本,如果已到达流末尾,则返回null。
3)从文件中读取数据
【示例】使用字符缓冲流从文件中读数据
public class BufferedCharDemo { public static void main(String[] args) throws IOException { // 创建基本字符输出流 Reader fr = new FileReader("demo.txt"); // 把基本的流进行包装 BufferedReader bw = new BufferedReader(fr); // 读取数据 String line= null; // 当返回值为null,则数据读取完毕 while((line = bw.readLine()) != null) { System.out.println(line); } // 关闭流 bw.close(); } } |
注意:关闭缓冲流时已经实现对字符流的关闭,所以此处我们只需要关闭缓冲流即可。
- readLine的底层实现
readLine()本质还是通过read()方法来实现的。方法内部定义出了一个临时容器(StringBuffer),在调用方法时,调用底层的read()方法读取一个字符,判断是不是换行符。如果不是,就把读取到的字符追加到临时的容器中;如果是换行符,就把容器中的数据返回,从而实现读取一行。
【示例】模拟readLine方法的实现
public class MyBufferedReader { // 字符输入流 private Reader reader; // 构造方法 public MyBufferedReader(Reader reader) { this.reader = reader; } /** * 读取一行文本 */ public String readLine() throws IOException { // 定义一个临时容器,用来存储数据 StringBuffer sb = new StringBuffer(); // 通过循环,一个字符一个字符的来读取数据 int ch = 0; while((ch = reader.read()) != -1) { // 因为在windows操作系统中,换行符由'\r'和'\n'组成 if(ch == '\r') continue; if(ch == '\n') // 当遇到换行符的时候,那么直接把容器中的数据返回 return sb.toString(); // 当没有遇到换行符号的时候,那么把数据追加到容器中 sb.append((char)ch); } // 当最后一行数据没有换行符的时候,我们手动返回最后一行数据 if(sb.length() != 0) return sb.toString(); return null; } /** * 关闭流 */ public void close() throws IOException { reader.close(); } } |
3.3 字符缓冲流拷贝案例
【示例】字符缓冲流拷贝案例
public class BufferedCharDemo { public static void main(String[] args) throws IOException { // 把基本输出流进行包装 BufferedReader br = new BufferedReader(new FileReader("E:\\Java\\集合框架.txt")); // 把基本输出流进行包装 BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\集合框架.txt")); // 读取数据 String line = null; while((line = br.readLine()) != null) { // 存数据 bw.write(line); // 刷新数据 bw.flush(); // 添加换行 bw.newLine(); } // 关闭流 br.close(); bw.close(); } } |