java io流學習總結

一、什麼是流?

流就是位元組序列的抽象概念,能被連續讀取數據的數據源和能被連續寫入數據的接收端就是流,流機制是Java及C++中的一個重要機制,通過流我們可以自由地控制檔案、記憶體、IO設備等數據的流向。而IO流就是用於處理設備上的數據,如:硬碟、記憶體、鍵盤錄入等。IO流根據處理類型的不同可分為位元組流和字元流,根據流向的不同可分為輸入流和輸出流。

二、位元組流和字元流的區別:

字元流,因為檔案編碼的不同,就有了對字元進行高效操作的字元流對象,它的原理就是基於位元組流讀取位元組時去查了指定的碼錶。它和位元組流的區別有兩點:1.在讀取數據的時候,位元組流讀到一個位元組就返回一個位元組,字元流使用了位元組流讀到一個或多個位元組(一個中文對應的位元組數是兩個,在UTF-8碼錶中是3個位元組)時,先去查指定的編碼表,再將查到的字元返回;2.位元組流可以處理所有類型的數據,如jpg、avi、mp3、wav等等,而字元流只能處理字元數據。所以可以根據處理的檔案不同考慮使用位元組流還是字元流,如果是純文本數據可以優先考慮字元流,否則使用位元組流。

三、IO體系,所具備的基本功能就是讀和寫:

1.字元流

|-- Reader(讀)

|-- Writer(寫)

Reader

|--InputStreamReader

|--FileReader:用於處理檔案的字元讀取流對象

Writer

|--OutputStreamWriter

|--FileWriter:用於處理檔案的字元寫入流對象

其實很容易就可以看出來,IO體系中的子類名後綴絕大部分是父類名稱,而前綴則是體現子類特有功能的名稱。

Reader中常見的方法:

|--int read

讀取一個字元,並返回讀到的這個字元,讀到流的末尾則返回-1。

|--int read(char)

將讀到的字元存入指定的數組中,返回的是讀到的字元個數,

讀到流的末尾則返回-1。

|--close

讀取字元其實用的是window系統的功能,就希望使用完畢後,

進行資源的釋放。

FileReader除了自己的構造函式外沒有特有的方法:

|--用於讀取文本檔案的流對象。

|--用於關聯文本檔案。

|--構造函式FileReader(String fileName)

在讀取流對象初始化時,必須要指定一個被讀取的檔案,

如果該檔案不存在則會發生FileNotFoundException異常。

Writer中常見的方法:

|--write

將一個字元寫入到流中。

|--write(char)

將一個字元數組寫入到流中。

|--writer(String)

將一個字元寫入到流中。

|--flush

刷新流,將流中的數據刷新到目的地中,流還存在。

|--close

關閉資源,在關閉錢會先調用flush, 刷新流中的數據到目的地。

FileWriter,除了自己的構造函式外沒有特有的方法:

|--該類的特點

|--用於處理文本檔案

|--沒有默認的編碼表

|--有臨時緩衝

|--構造函式,在寫入流對象初始化時,必須要有一個存儲數據的目的地。

|--FileWriter(String fileName),該構造器是乾什麼用的呢?

|--調用系統資源

|--在指定位置創建一個檔案,如果該檔案已經存在則被覆蓋。

|--FileWriter(String filename,Boolean append),這構造器的作用是?

當傳入的boolean類型的值為true時,會在指定檔案末尾處進行數據的續寫。

清單1,將文本數據保存到檔案中代碼

private static void test1{

FileWriter fw=null;

try {

//初始化FileWriter對象,指定檔案名稱已經存儲路徑

fw=new FileWriter("D:/test.txt");

fw.write("將字元串寫入流");

//將流中的數據刷新到目的地,流還在

fw.flush;

fw.write("將字元串寫入流");

} catch (IOException e) {

e.printStackTrace;

}finally{

if(fw!=null){

try {

fw.close;

} catch (IOException e1) {

e1.printStackTrace;

}

}

}

}

清單2,讀取一個已有文本檔案,並將文本內容列印出來代碼

private static void test2{

FileReader fr=null;

try {

//初始化FileReader對象,指定檔案路徑

fr=new FileReader("D:/test.txt");

int ch=0;

while((ch=fr.read)!=-1){

//每次讀取一個字元,直到讀到末尾-1為止

System.out.println((char)ch);

}

} catch (IOException e) {

e.printStackTrace;

}finally{

if(fr!=null){

try {

fr.close;

} catch (IOException e1) {

e1.printStackTrace;

}

}

}

}

這樣每讀到一個字元就列印出來,效率很不高,能不能按指定大小讀取完後再列印出來呢?答案是當然可以的。

清單3,讀取一個已有文本檔案,讀完1kb再將其讀到的內容列印出來代碼

private static void test3{

FileReader fr=null;

try {

//初始化FileReader對象,指定檔案路徑

fr=new FileReader("D:/test.txt");

char buf=new char[1024];

int len=0;

while((len=fr.read(buf))!=-1){

//每次讀取1kb大小的字元,直到讀到末尾-1為止

System.out.println(new String(buf,0,len));

}

} catch (IOException e) {

e.printStackTrace;

}finally{

if(fr!=null){

try {

fr.close;

} catch (IOException e1) {

e1.printStackTrace;

}

}

}

}

字元流的緩衝區:

|--緩衝區的出現提高了對流的操作效率。

原理:其實就是將數組進行封裝。

|--對應的對象

|--BufferedWriter

特有方法newLine,跨平台的換行符。

|--BufferedReader

特有方法readLine,一次讀一行,到行標記時,將行標記

之前的字元數據作為字元串返回,讀到末尾返回null。

|--說明

在使用緩衝區對象時,要明確,緩衝的存在是為了增強流

的功能而存在,所以在建立緩衝區對象時,要先有流對象

存在。其實緩衝區內部就是在使用流對象的方法,只不過

加入了數組對數據進行了臨時存儲,為了提高運算元據的

效率。

|--代碼上的體現

|--寫入緩衝區對象

根據前面所說的建立緩衝區時要先有流對象,並將其作為參數傳遞給緩衝區的構造函式

BufferedWriter bufw=new BufferedWriter(new FileWriter(“test.txt”));

bufw.write(“將數據寫入緩衝區”);

bufw.flush;//將緩衝區的數據刷新到目的地

bufw.close;//其實關閉的是被包裝在內部的流對象

|--讀取緩衝區對象

BufferedReader bufr=new BufferedReader(new FileReader(“test.txt”));

String line=null;

while((line=bufr.readLine)!=null){

//每次讀取一行,取出的數據不包含回車符

system.out.println(line);

}

bufr.close;

清單4,使用緩衝區對文本檔案進行拷貝代碼

private static void test4{

BufferedReader bufr=null;

BufferedWriter bufw=null;

try {

bufr=new BufferedReader(new FileReader("D:/a.txt"));

bufw=new BufferedWriter(new FileWriter("D:/b.txt"));

String line=null;

while((line=bufr.readLine)!=null){

bufw.write(line);//每次將一行寫入緩衝區

bufw.flush;//刷新到目的地

}

} catch (IOException e) {

e.printStackTrace;

}finally{

try {

if(bufw!=null){

bufw.close;

}

if(bufr!=null){

bufr.close;

}

} catch (IOException e1) {

e1.printStackTrace;

}

}

}

仔細看可以發現,程式裡面的FileReader對象和FileWriter對象直接new出來且沒有調用close,因為緩衝對象調用了這兩個方法,前面說了,緩衝對象調用的flush和close其實就是關閉被包裝在其內部的流對象。關閉流的先後順序也要注意,如果流之間有依賴關係,則被依賴的流要後關閉。readLine方法原理:其實緩衝區中的該方法,用的還是與緩衝區關聯的流對象的read方法,只不過,每一次讀到一個字元先不進行具體操作,先進行臨時存儲,當讀到回車標記時,將臨時容器中存儲的數據一次性返回。我們可以根據這個原理來自己編寫一個緩衝區對象。

清單5,編寫一個自己的bufferedreader代碼

public class MyBufferedReader {

private Reader reader;

public MyBufferedReader(Reader reader){

this.reader=reader;

}

public String readLine throws IOException{

StringBuilder sb=new StringBuilder;

int ch=0;

while((ch=reader.read)!=-1){

if(ch=='\r'){//空格則繼續

continue;

}else if(ch=='\n'){//每次返回一行

return sb.toString;

}else{

sb.append((char)ch);

}

}

return sb.toString;

}

public void close throws IOException{

//緩衝對象的關閉方法其實就是調用流本身的close

reader.close;

}

}

測試時把清單4的BufferedReader對象替換成MyBufferedReader對象即可。

清單6,測試mybufferedreader代碼

private static void test4{

MyBufferedReader bufr=null;

BufferedWriter bufw=null;

try {

bufr=new MyBufferedReader(new FileReader("D:/a.txt"));

bufw=new BufferedWriter(new FileWriter("D:/b.txt"));

String line=null;

while((line=bufr.readLine)!=null){

bufw.write(line);//每次將一行寫入緩衝區

bufw.flush;//刷新到目的地

}

} catch (IOException e) {

e.printStackTrace;

}finally{

try {

if(bufw!=null){

bufw.close;

}

if(bufr!=null){

bufr.close;

}

} catch (IOException e1) {

e1.printStackTrace;

}

}

}

其實我們自己寫的這個快取對象就是對Reader對象進行了功能的增強,Reader對象每次只能返回一個字元,而增強了功能之後該類就可以每次返回一行字元,也就是設計模式中所說的裝飾模式。