第四十四讲 I/O流——字符缓冲流的原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yerenyuan_pku/article/details/84573843

上一讲中已经介绍完了字符流的两个缓冲区对象——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();
	}

}

猜你喜欢

转载自blog.csdn.net/yerenyuan_pku/article/details/84573843