本篇將對JAVA I/O流中的字符輸入流Reader做個簡單的概括,先看下Reader的組成結構(下圖);

總得來說,以上每個字符流類都有一個對應的用途,可以如下劃分:
下面將對各個字符輸入流做個詳細介紹;
InputStreamReader繼承字符流抽象類Reader,每個構造方法都包含一個字節流InputStream入參,功能顯而易見,就是通過字節流InputStream得到字符流;

PS:無論是構造方法裡對字節流的處理(StreamDecoder.forInputStreamReader),還是字符流的讀取(StreamDecoder.read),底層都是通過StreamDecoder類實現的,有興趣的可以深入了解~
舉個例子,如下,為方便閱讀,不做異常處理:
package io;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
//通過“標准”字節輸入流得到一個字符輸入流
Reader reader = new InputStreamReader(System.in);
//讀取一個字符並打印
System.out.println((char)reader.read());
//關閉流
reader.close();
}
}
//輸出示例
//鍵盤輸入0
//顯示0
FileReader繼承字符流InputStreamReader,通過本地字符文件得到字符流,查看其構造方法,都是先根據傳入的參數生成一個FileInputStream字節流對象,然後調用父類InputStreamReader的構造方法得到字符流;

舉個簡單的例子:
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
//讀取本地文件,得到字符流
Reader reader = new FileReader("C:\\test.txt");
//讀取一個字符並打印
System.out.println((char)reader.read());
//關閉流
reader.close();
}
}
CharArrayReader可以讓我們通過字符數組得到字符流,構造方法如下

查看源碼,可以發現在CharArrayReader裡面直接使用外部字符數組的引用,也就是說,即使得到字符流對象後,當改變外部數組時,通過流讀取的字符也會改變,如下代碼實例:
package io;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
//字符數組
char[] charArr = new char[]{'a', 'b', 'c'};
//通過內存中的字符數組,得到字符流
Reader reader = new CharArrayReader(charArr);
//改變數組charArr的元素值
charArr[0] = 'e';
//讀取一個字符並打印
System.out.println((char)reader.read());//打印改變後的字符'e'
//關閉流
reader.close();
}
}
可以通過PipedWriter和PipedReader創建字符流管道,線程間可以通過管道進行通信,注意:一定要同一個JVM中的兩個線程;
PipedReader一般是要和PipedWriter配合使用的,其中一個線程通過PipedWriter往管道寫數據,另一個線程通過管道讀數據,注意讀寫都會阻塞線程,如下示例:
package io;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class IOTest {
public static void main(String[] args) throws IOException {
final PipedWriter pw = new PipedWriter();
final PipedReader pr = new PipedReader(pw);
ExecutorService es = Executors.newFixedThreadPool(2);
es.execute(new Runnable() {
@Override
public void run() {
try {
pw.write("hello~");
} catch (IOException e) {
e.printStackTrace();
}
}
});
es.execute(new Runnable() {
@Override
public void run() {
char[] cbuffer = new char[6];
try {
// 會導致線程阻塞
pr.read(cbuffer, 0, 6);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(cbuffer);
}
});
}
}
這個字符流可以用來裝飾其它字符輸入流,可以為其它字符輸入流提供字符緩沖區,避免每次從外部媒介中讀取數據,這裡用到了設計模式裡的裝飾器模式,可以參考我之前寫的,
http://www.cnblogs.com/chenpi/p/5173818.html
被裝飾的字符流可以有更多的行為,比如readLine方法等;
舉個使用的例子,讀取外部文件:
package io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
//讀取本地文件,得到字符流,這裡設定緩沖區大小為10k
BufferedReader reader = new BufferedReader(new FileReader("C:\\test.txt"), 10 * 1024);
//讀取一行
System.out.println(reader.readLine());
System.out.println(reader.readLine());
System.out.println(reader.readLine());
//關閉流
reader.close();
}
}
繼承BufferedReader,除了BufferedReader提供的功能外,還多了獲取行號信息,
注意,LineNumberReader的setLineNumber方法並不能實現流的跳躍讀取
舉個例子
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class IOTest {
public static void main(String[] args) throws IOException {
//讀取本地文件,得到字符流,這裡設定緩沖區大小為10k
LineNumberReader reader = new LineNumberReader(new FileReader("C:\\test.txt"), 10 * 1024);
System.out.println(reader.getLineNumber());//打印當前行號
System.out.println(reader.readLine());//讀取一行
System.out.println(reader.getLineNumber());
System.out.println(reader.readLine());//讀取一行
//關閉流
reader.close();
}
}
抽象類FilterReader是實現自定義過濾輸入字符流的基類,從源碼的實現上來看,僅僅只是簡單覆蓋了Reader中的所有方法,感覺沒什麼卵用,因為已經有一個抽象類Reader了;
簡單的說,PushbackReader字符流允許你在讀取字符的過程中,通過調用unread方法往緩沖區插入字符,下次讀取的時候,先從緩沖區讀數據,緩沖區沒數據再從流中讀取;
該類同樣使用了裝飾器模式;
一般可以用PushbackReader完成讀取回退功能,比如把之前讀取的字符通過調用unread方法插入到緩沖區中,下次再讀取的時候,就又可以讀取到之前的數據了;
舉個例子如下:
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;
public class IOTest {
public static void main(String[] args) throws IOException {
// 讀取本地文件,得到字符流,這裡設定緩沖區大小為1024k
PushbackReader reader = new PushbackReader(new FileReader("C:\\test.txt"), 1024);
// 讀取一個字符
char a = (char) reader.read();
System.out.println(a);
// 將讀取的字符推回到流中
// 其實說的簡單點,就是將字符a存到緩沖區中,下次讀取的時候先從緩沖區讀數據,緩沖區沒數據再從流中讀取
reader.unread(a);
// 重新讀取之前的字(在緩沖區中)
System.out.println((char) reader.read());
// 從流中讀取數據
System.out.println((char) reader.read());
// 關閉流
reader.close();
}
}
將字符串轉為字符輸入流,比較簡單,看個使用例子如下:
package io;
import java.io.IOException;
import java.io.StringReader;
public class IOTest {
public static void main(String[] args) throws IOException {
// 通過字符串,得到字符流
StringReader reader = new StringReader("hello~");
// 從流中讀取數據
char[] cbuf = new char[6];
reader.read(cbuf, 0, 6);
System.out.println(cbuf);
// 關閉流
reader.close();
}
}