本篇將對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(); } }