輸入流和輸出流是相對於記憶體裝置而言
即將外設中的資料讀取到記憶體中就是輸入
將記憶體中的資料寫入到外設中就是輸出
字元流的由來:
其實就是:位元組流讀取文字位元組資料後,不直接操作而是先查指定的編碼表,獲取對應的文字。
再對這個文字進行操作,其實就是位元組流+編碼表
位元組流的兩個頂層父類:
1,InputStream 2,OutputStream
字元流的兩個頂層父類:
1,Reader 2,Writer
這些體系的子類都以父類名作為字尾。
而子類名的字首就是該物件的功能。
如果要操作文字資料,建議優先考慮字元流。
而且要將資料從記憶體寫到硬碟上。要使用字元流中的輸出流,Writer
輸出流程式碼演示:
import java.io.FileWriter; import java.io.IOException; public class IODemo { private static final String LINE_SPEARATOR = System.getProperty("line.separator"); /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /** * 建立一個可以往檔案中寫入字元資料的字元輸出流物件 * 既然是往一個檔案中寫入文字資料,那麼在建立物件時,就必須明確該物件檔案(用於儲存資料的目的地) * 如果檔案不存在,則建立。 * 如果檔案存在,則覆蓋 * * 如果建構函式中加入ture,可以實現檔案的續寫。 */ FileWriter wf = new FileWriter("E:\\Workspaces\\MyEclipse 10\\a.txt",true); /** * 呼叫Writer物件中的writer(String s)方法,寫入資料。 * 其實資料寫入到臨時儲存緩衝區中。 */ wf.write("my data"+LINE_SPEARATOR+"again line "); /** * 進行重新整理,將資料直接寫到目的地中。 */ wf.flush(); /** * 關閉流,關閉資源。在關閉前會呼叫flush重新整理緩衝中的資料到目的地中。 * 如果在關閉流後再進行flush方法的呼叫,則會導致IOException * 如需要繼續寫入,則使用flush進行資料寫入,如果不需要再寫資料則直接呼叫close即可。 * */ wf.close(); } }
輸入流程式碼演示一:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo1 { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 1,建立一個能夠讀取字元流資料的流物件 /** * 在建立讀取劉物件時,必須要明確被讀取的檔案,一定要確定該檔案是存在的。 * * 用一個讀取流關聯一個已存在的檔案。 */ FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); /** * 用reader中的read方法讀取 * 簡單讀取,一個字元一個字元的讀。 */ int ch = 0; while((ch=fr.read())!=-1){ System.out.println((char)ch); } fr.close(); } }
輸入流程式碼演示二:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo2{ /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); /** * 使用read(char[] ch)讀取文字檔案資料。 * 先建立字元陣列 */ char[] buf = new char[1024]; int len = 0; while((len = fr.read(buf))!=-1){//將讀到的字元儲存到陣列中。 System.out.println(new String(buf)); } fr.close(); } }
複製功能實現:
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyTextDemo { private static final int BUFFER_SIZE = 1024; /** * @param args */ public static void main(String[] args) { FileReader fr = null; FileWriter fw = null; try { fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); fw = new FileWriter("E:\\Workspaces\\MyEclipse 10\\b.txt"); //建立一個臨時容器,用於快取讀取到的字元 char[] buf = new char[BUFFER_SIZE]; //定義一個變數記錄讀取到的字元數其實就是往陣列裡裝的字元個數。 int len = 0; while((len = fr.read(buf))!= -1){ fw.write(buf,0,len); } } catch (Exception e) { throw new RuntimeException("失敗了"); }finally{ if(fw!=null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } if(fr!=null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
緩衝區的寫入使用程式碼演示:
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class BufferedWriterDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("E:\\Workspaces\\MyEclipse 10\\b.txt"); //為了提高寫入的效率,使用了字元流的緩衝區。 //建立了一個字元寫入流的緩衝區物件,並和指定要被緩衝的流物件相關聯 BufferedWriter bufw = new BufferedWriter(fw); //使用緩衝區的寫入方法將資料先寫入到緩衝區中 bufw.write("buffered write"); bufw.newLine(); bufw.write("again line"); //使用緩衝區的重新整理方法將資料刷到目的地中。 bufw.flush(); //關閉緩衝區。其實關閉的就是被緩衝的流物件。 bufw.close(); } }
緩衝區讀取程式碼演示:
FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null){ System.out.println(line); }
BufferedReader原理程式碼演示:
import java.io.FileReader; import java.io.IOException; public class MyBufferedReader { /** * @param args * 自定義的讀取緩衝區,模擬一個BufferedReader. * 分析: * 緩衝區中無非就是封裝了一個陣列, * 並對外提供了更多的方法對陣列進行訪問, * 其實這些方法最終操作的都是陣列的角標 * * 緩衝的原理: * 其實就是從源中獲取一批資料裝進緩衝區中 * 在從緩衝區中不斷的取出一個一個資料。 * * 當此次取完後,再從源中繼續取一批資料進緩衝區。 * 當源中的資料取光時,用-1作為結束標記。 * */ private FileReader r; private char[] buf = new char[1024];//定義一個陣列作為緩衝區 //定義一個指標用於操作這個陣列中的元素,當操作到最後一個元素後,指標應該歸零。 private int pos = 0; //定義一個計數器,用於記錄緩衝區中的資料個數。當該資料減到0,就從源中繼續獲取資料到緩衝區中。 private int count = 0; MyBufferedReader(FileReader r){ this.r = r; } /** * 該方法從緩衝區中一次取一個字元 * @return * @throws IOException */ public int myRead() throws IOException{ //從源中獲取一批資料到緩衝區中。需要先做判斷,只有計數器為0時,才需要從源中獲取資料。 /*if(count==0){ count = r.read(buf); if(count<0){ return -1; } pos = 0;//每次獲取資料到緩衝區後,角標歸0 char ch = buf[pos]; pos++; count--; return ch; }else if(count>0){ char ch = buf[pos]; pos++; count--; return ch; } */ //方法簡化重寫 if(count==0){ count = r.read(buf); pos = 0; } if(count<0){ return -1; } char ch = buf[pos]; pos++; count--; return ch; } public String myReadLine() throws IOException{ StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = myRead())!=-1){ if(ch == '\r'){ continue; } if(ch == '\n'){ return sb.toString(); } //將從緩衝區中讀到的字元儲存到快取行資料的緩衝區中。 sb.append((char)ch); } if(sb.length()!=0){ return sb.toString(); } return null; } }
IO緩衝區之裝飾設計模式:
當對一組物件的功能進行增強時,就可以使用該模式進行問題的解決。
裝飾和繼承都能夠實現一樣的特點:進行功能的擴充套件和增強。
區別在於:
首先有一個整合體系。
假設體系如下:
Writer
--TextWriter:用於操作文字
--MediaWriter:用於操作媒體
想要對操作的動作進行效率的提高。
按照物件導向,可以通過繼承對具體的進行功能的擴充套件。
效率提高需要加入緩衝技術。
Writer
--TextWriter:用於操作文字
--BufferTextWriter:加入了緩衝技術的操作文字的物件。
--MediaWriter:用於操作媒體
到這裡其實就已經實現了,但是並不理想。
如果這個體系進行功能擴充套件,那麼就又多了流物件。
那麼這個流要提高效率,就一定要產生子類,這時就會發現只為提高功能而進行的繼承就會導致繼承體系越來越臃腫。就不夠靈活。
那麼重新進行思考:
既然加入的都是同一種技術---緩衝。
前一種是讓緩衝和具體的流物件相結合。
那麼我們就對緩衝進行單獨的封裝,哪個物件需要緩衝就將哪個物件和緩衝關聯。
class Buffer{
Buffer(TextWriter w){}
Buffer(MediaWriter w){}
}
class BufferWriter extends Writer{
Buffer(Writer w){}
}
那麼體系就變成如下結構
Writer
--TextWriter:用於操作文字
--MediaWriter:用於操作媒體
--BufferWriter:用於提高效率的。
結論如下:
裝飾比繼承更為靈活。
特點:裝飾類和被裝飾類都必須所屬同一個介面或者父類。
裝飾設計模式思想重寫MyBufferedReader
import java.io.IOException; import java.io.Reader; public class MyBufferedReader extends Reader { private Reader r; private char[] buf = new char[1024]; private int pos = 0; private int count = 0; MyBufferedReader(Reader r){ this.r = r; } public int myRead() throws IOException{ if(count==0){ count = r.read(buf); pos = 0; } if(count<0){ return -1; } char ch = buf[pos]; pos++; count--; return ch; } public String myReadLine() throws IOException{ StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = myRead())!=-1){ if(ch == '\r'){ continue; } if(ch == '\n'){ return sb.toString(); } sb.append((char)ch); } if(sb.length()!=0){ return sb.toString(); } return null; } @Override public int read(char[] cbuf, int off, int len) throws IOException { return 0; } @Override public void close() throws IOException { r.close(); } }