第四十四講 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();
}
}