本文粗略的介紹下JavaIO的整體框架,重在解釋BufferReader/BufferWriter的演變過程和原理(對應的設計模式)
一.JavaIO的簡介
流按操作數據分為兩種:字節流與字符流.
流按流向分為:輸入流(讀),輸出流(寫)。
字符流由來就是:早期的字節流+編碼表,為了更便於操作文字數據。
記住:只要是操作字符數據,應該優先使用字符流。
字節流的抽象基類:InputStream ,OutputStream。
字符流的抽象基類:Reader , Writer。
二.JavaIO中的流對象的繼承和字節流,字符流的對應關系.
InputStream字節輸入流:

OutputStream:字節輸出流:

InputStream與OutputStream之間的對應關系:

Reader :字符輸入流

Writer :字符輸出流
Reader與Writer之間的對應關系
輸入字節流、輸入字符流之間對應關系
輸出字節流、輸出字符流之間對應關系
轉換流 :InputStreamReader,OutputStreamWriter
轉換流的由來:字符流與字節流之間的橋梁 ,方便了字符流與字節流之間的操作
轉換流的應用:字節流中的數據都是字符時,轉成字符流操作更高效。
標准輸入輸出流 :
System類中的字段:in,out。它們各代表了系統標准的輸入和輸出設備,默認輸入設備是鍵盤,輸出設備是顯示器。
System.in的類型是InputStream.
System.out的類型是PrintStream是OutputStream的子類FilterOutputStream 的子類.
舉例引入:從原始IO----->用字符數組作為緩沖區---->用IO中的BufferReader/BufferWriter----->JavaIO中的設計模式(裝飾設計模式)
①使用最原始的方式拷貝方式代碼:
1 /* 2 * 需求:作業:將c盤的一個文本文件復制到d盤。 3 * 4 * 思路: 5 * 1,需要讀取源, 6 * 2,將讀到的源數據寫入到目的地。 7 * 3,既然是操作文本數據,使用字符流。 8 * 9 */ 10 public class CopyTextTest { 11 public static void main(String[] args) throws IOException { 12 //1,讀取一個已有的文本文件,使用字符讀取流和文件相關聯。 13 FileReader fr = new FileReader("IO流_2.txt"); 14 //2,創建一個目的,用於存儲讀到數據。 15 FileWriter fw = new FileWriter("copytext_1.txt"); 16 //3,頻繁的讀寫操作。 17 int ch = 0; 18 while((ch=fr.read())!=-1){ 19 fw.write(ch); 20 } 21 //4,關閉流資源。 22 fw.close(); 23 fr.close(); 24 } 25 }
②引入字符數組作為緩沖區:(循環次數小,效率高)
1 public class CopyTextTest_2 { 2 private static final int BUFFER_SIZE = 1024; 3 public static void main(String[] args) { 4 FileReader fr = null; 5 FileWriter fw = null; 6 try { 7 fr = new FileReader("IO流_2.txt"); 8 fw = new FileWriter("copytest_2.txt"); 9 //創建一個臨時容器,用於緩存讀取到的字符。 10 char[] buf = new char[BUFFER_SIZE];//這就是緩沖區。 11 //定義一個變量記錄讀取到的字符數,(其實就是往數組里裝的字符個數) 12 int len = 0; 13 while((len=fr.read(buf))!=-1){ 14 fw.write(buf, 0, len); 15 } 16 } catch (Exception e) { 17 // System.out.println("讀寫失敗"); 18 throw new RuntimeException("讀寫失敗"); 19 }finally{ 20 if(fw!=null) 21 try { 22 fw.close(); 23 } catch (IOException e) { 24 25 e.printStackTrace(); 26 } 27 if(fr!=null) 28 try { 29 fr.close(); 30 } catch (IOException e) { 31 32 e.printStackTrace(); 33 } 34 } 35 } 36 }
原理圖:

緩沖區的出現提高了文件的讀寫效率,有緩沖區可以提高效率,在Java中把緩沖區進行了封裝,關閉緩沖區就是關閉的被緩沖的流對象!所以只需要關閉緩沖區就可以,不必要再關閉流了。
③引入BufferWriter(緩沖區的出現提高了對數據的讀寫效率,緩沖區要結合流才可以使用,在流的基礎上對流的功能進行了增強)
1 public class CopyTextByBufTest { 2 public static void main(String[] args) throws IOException { 3 FileReader fr = new FileReader("buf.txt"); 4 BufferedReader bufr = new BufferedReader(fr); 5 6 FileWriter fw = new FileWriter("buf_copy.txt"); 7 BufferedWriter bufw = new BufferedWriter(fw); 8 9 String line = null; 10 while((line=bufr.readLine())!=null){ 11 bufw.write(line); 12 bufw.newLine(); 13 bufw.flush(); 14 } 15 /* 16 int ch = 0; 17 while((ch=bufr.read())!=-1){ 18 bufw.write(ch); 19 } 20 */ 21 bufw.close(); 22 bufr.close(); 23 } 24 }
字符流緩沖區:
BufferedWriter:newLine();
BufferedReader: readLine();
Buffer***的原理圖

④☆☆☆裝飾設計模式
裝飾設計模式的簡易代碼:
1 public class PersonDemo { 2 public static void main(String[] args){ 3 Person p = new Person(); 4 p.chifan(); 5 NewPerson p1 = new NewPerson(p); 6 p1.chifan(); 7 NewPerson2 p2 = new NewPerson2(); 8 p2.chifan(); 9 } 10 } 11 class Person{ 12 void chifan(){ 13 System.out.println("吃飯"); 14 } 15 } 16 //這個類的出現是為了增強Person而出現的。 17 class NewPerson{ 18 private Person p ; 19 NewPerson(Person p){ 20 this.p = p; 21 } 22 public void chifan(){ 23 System.out.println("開胃酒"); 24 p.chifan(); 25 System.out.println("甜點"); 26 } 27 } 28 class NewPerson2 extends Person{ 29 public void chifan(){ 30 System.out.println("開胃酒"); 31 super.chifan(); 32 System.out.println("甜點"); 33 } 34 }
NewPerson是對Person采用了裝飾設計模式對Person對象的功能,NewPerson2是繼承了Person,對對象的功能進行增強。
裝飾和繼承都能實現一樣的特點:進行功能的擴展增強,但是他們之前是有區別的,裝飾更加靈活。
程序輸出:
吃飯
開胃酒
吃飯
甜點
開胃酒
吃飯
甜點
對以上的代碼的具體分析貼圖:

裝飾和繼承都能實現一樣的特點:對類對象的功能的擴展增強,區別有哪些?
假設首先有一個繼承體系如下:(TextWriter,MediaWriter並不存在)
Writer |--TextWriter:用於操作文本 |--MediaWriter:用於操作媒體。
想要對操作的動作進行效率的提高。按照面向對象,可以通過繼承對具體的進行功能的擴展,效率提高需要加入緩沖技術,上面的體系結構變成如下:
Writer |--TextWriter:用於操作文本 |--BufferTextWriter:加入了緩沖技術的操作文本的對象。 |--MediaWriter:用於操作媒體。 |--BufferMediaWriter:
到這里就可以了,能達到對功能增強的目標,但是這樣做好像並不理想。
如果這個體系進行功能擴展,又多了一些其他的流對象(****Writer,****Reader。。。。)
那么這個流要提高效率,是不是也要產生子類呢?
答案是:是。這時就會發現只為提高功能,進行的繼承,導致繼承體系越來越臃腫,不夠靈活。
重新思考這個問題?
既然加入的都是同一種技術--緩沖。
前一種是讓緩沖和具體的對象相結合。
可不可以將緩沖進行單獨的封裝,哪個對象需要緩沖就將哪個對象和緩沖關聯。
通過上面的代碼的分析,可以使用裝飾設計模式的思想:
class Buffer{ Buffer(TextWriter w) { } Buffer(MediaWirter w) { } }
這樣Buffer僅僅對傳入的TextWriter和MediaWriter進行操作,下面傳入一個Writer,就對Writer中的所有子類進行了操作。
//緩沖對象進行的也是寫的操作,所以要繼承Writer class BufferWriter extends Writer{ BufferWriter(Writer w) { } }
這樣Writer的體系結構變成如下:
Writer |--TextWriter:用於操作文本 |--MediaWriter:用於操作媒體。 |--BufferWriter:用於提高效率。
和上面最開始的通過繼承增強功能的方式相比:
這兩個體系相比,裝飾比繼承靈活,如果想對已有體系進行功能的擴展,首先要想到的就是裝飾模式
裝飾模式的特點:裝飾類和被裝飾類都必須所屬同一個接口或者父類。


字節流和字符流的區別:
字節流能處理的數據單元不一樣,數據的格式不一樣,MP3,文本等。字符流只能操作文字。
字符流用的是緩沖區是字符數組。字節流用的緩沖區是字節數組。
字節流一次就不能讀取出一個中文文字。字符流可以。
能用字符流進行媒體文件的操作嗎?
字符流的特點在於,讀取完字節之后並沒有直接去往目的地里面去寫而是去查表(查表有對應的數據,然后我們接着寫不一樣嗎,是的,真的是這樣的,但就是這個地方出了問題),萬一讀到的這個字節數據在表里查不到內容呢?文字有特定的編碼格式,而這些媒體文件沒有,他們都有其自身的編碼方式,而且這些編碼方式都是千變萬化的,他拿到碼表去查沒有找到對應的,怎么辦?碼表會拿一些未知字符區的數據來表示這個沒有對應的情況,就直接寫到目的數據里面去了,這樣元數據和目的數據就不一致了,這樣就不能被圖片編輯器所解析,解析不了。
不要嘗試用字符流去操作媒體文件,你操作完之后有可能發現操作之后的數據大小和源數據的數據大小不一致。




