1. 程式人生 > >第四十四講 I/O流——字元緩衝流的原理

第四十四講 I/O流——字元緩衝流的原理

上一講中已經介紹完了字元流的兩個緩衝區物件——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();
	}

}