上一讲中已经介绍完了字符流的两个缓冲区对象——BufferedWriter和BufferedReader,而缓冲区的原理我们并没搞明白,本文就来揭示其真正面目。
缓冲区的原理
临时存储数据,减少了对设备操作的频率,提高了效率,其实就是将数据临时缓存到了内存(数组)中。
BufferReader类的read方法实现原理
下面我们就来分别模拟BufferReader类的read方法实现原理和其readLine方法实现原理。有这样一个需求:我们知道BufferReader类中有一个read方法,现在要自定义一个类中包含一个功能和read()一致的方法,来模拟一下BufferReader。
这是我编写的一个我自己的MyBufferedReader类,并在其中实现了一个myRead()方法来模拟BufferReader类的read()方法,示例代码如下:
package cn.liayun.buffer;
import java.io.IOException;
import java.io.Reader;
public class MyBufferedReader {
//1,持有一个流对象
private Reader r;
//2,因为是缓冲区对象,所以内部必然维护一个数组
private char[] buffer = new char[1024];
//3,定义一个角标
int index = 0;
//4,定义变量记录住数组元素的个数
private int count = 0;
//5,一初始化,就必须明确被缓冲的对象
public MyBufferedReader(Reader r) {
super();
this.r = r;
}
/**
* 读取一个字符的方法,而且是高效的
* @throws IOException
*/
public int myRead() throws IOException {
if (count == 0) {
//通过被缓冲流对象的read方法,就可以将设备上的数据存储到数组中
count = r.read(buffer);
index = 0;
}
if (count < 0) {
return -1;
}
char ch = buffer[index];
index++;//角标每取一次,都要自增。
count--;//既然取出一个,数组的数量就要减少,一旦减到0,就再从设备上获取一批数据,存储到数组中。
return ch;
}
}
你可以借助下图来理解以上myRead方法:
BufferReader类的readLine方法实现原理
BufferReader类的readLine方法实现原理其实就是调用read方法从缓冲区中获取数据,然后存储到readLine方法自己的StringBuilder缓冲区中,它会判断行终止符,只存储行结束符前的数据。下面我接着在MyBufferedReader类中实现了一个myReadLine方法来模拟BufferReader类的read方法。
package cn.liayun.buffer;
import java.io.IOException;
import java.io.Reader;
public class MyBufferedReader {
//1,持有一个流对象
private Reader r;
//2,因为是缓冲区对象,所以内部必然维护一个数组
private char[] buffer = new char[1024];
//3,定义一个角标
int index = 0;
//4,定义变量记录住数组元素的个数
private int count = 0;
//5,一初始化,就必须明确被缓冲的对象
public MyBufferedReader(Reader r) {
super();
this.r = r;
}
/**
* 读取一个字符的方法,而且是高效的
* @throws IOException
*/
public int myRead() throws IOException {
if (count == 0) {
//通过被缓冲流对象的read方法,就可以将设备上的数据存储到数组中
count = r.read(buffer);
index = 0;
}
if (count < 0) {
return -1;
}
char ch = buffer[index];
index++;//角标每取一次,都要自增。
count--;//既然取出一个,数组的数量就要减少,一旦减到0,就再从设备上获取一批数据,存储到数组中。
return ch;
}
/**
* 读取一行文本
* @throws IOException
*/
public String myReadLine() throws IOException {
//1,定义一个临时缓冲区,用于存储一行文本
StringBuilder sb = new StringBuilder();
//2,不断地调用myRead方法从缓冲区中取出数据
int ch = 0;
while ((ch = myRead()) != -1) {
//在存储前要判断行终止符
if (ch == '\r') {
continue;
}
if (ch == '\n') {
return sb.toString();
}
//对读取到的字符进行临时存储
sb.append((char)ch);
}
//如果文本结尾处有数据,但没有行结束符
//数据已被读到并存储到了StringBuilder中,只要判断StringBuilder的长度即可
if (sb.length() != 0) {
return sb.toString();
}
return null;
}
/**
* 定义一个缓冲区的关闭方法
* @throws IOException
*/
public void myClose() throws IOException {
//其实就是在关闭被缓冲的流对象
r.close();
}
}
从以上代码中还可看到实现了一个字符缓冲流的关闭方法,其实就是调用被缓冲流对象的close方法。
测试自定义的MyBufferedReader类
上面我编写完了一个MyBufferedReader类,并在其中实现了myRead方法和myReadLine方法来模拟BufferReader类的read方法和readLine方法,下面就来对该类进行测试。
package cn.liayun.buffer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("IO流_3.txt");
MyBufferedReader myBufr = new MyBufferedReader(fr);
String line = null;
while ((line = myBufr.myReadLine()) != null) {
System.out.println(line);
}
myBufr.myClose();
/*
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while ((line = bufr.readLine()) != null) {
System.out.println(line);
}
bufr.close();
*/
}
}
模拟一个带行号的字符缓冲流
由于上面我编写出了一个自定义的字符缓冲流(MyBufferedReader类),所以这里只须继承该类,然后覆盖父类读一行的方法即可。
package cn.liayun.buffer;
import java.io.IOException;
import java.io.Reader;
public class MyLineNumberReader extends MyBufferedReader {
// 定义一个计数器
private int lineNumber;
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
public MyLineNumberReader(Reader r) {
super(r);
}
/**
* 覆盖父类的读一行的方法
* @throws IOException
*/
public String myReadLine() throws IOException {
lineNumber++;//每读一行,行号自增
return super.myReadLine();
}
}
测试自定义类MyLineNumberReader。
package cn.liayun.buffer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("IO流_3.txt");
MyLineNumberReader myBufr = new MyLineNumberReader(fr);
String line = null;
while ((line = myBufr.myReadLine()) != null) {
System.out.println(myBufr.getLineNumber() + ":" + line);
}
myBufr.myClose();
}
}