IO流:用於處理設備上數據。
流:可以理解數據的流動,就是一個數據流。IO流最終要以對象來體現,對象都存在IO包中。
流也進行分類:
1:輸入流(讀)和輸出流(寫)。
2:因爲處理的數據不同,分爲字節流和字符流。
字節流:處理字節數據的流對象。設備上的數據無論是圖片或者dvd,文字,它們都以二進制存儲的。二進制的最終都是以一個8位爲數據單元進行體現,所以計算機中的最小數據單元就是字節。意味着,字節流可以處理設備上的所有數據,所以字節流一樣可以處理字符數據。
那麼爲什麼要有字符流呢?因爲字符每個國家都不一樣,所以涉及到了字符編碼問題,那麼GBK編碼的中文用unicode編碼解析是有問題的,所以需要獲取中文字節數據的同時+指定的編碼表纔可以解析正確數據。爲了方便於文字的解析,所以將字節流和編碼表封裝成對象,這個對象就是字符流。只要操作字符數據,優先考慮使用字符流體系。
注意:流的操作只有兩種:讀和寫。
流的體系因爲功能不同,但是有共性內容,不斷抽取,形成繼承體系。該體系一共有四個基類,而且都是抽象類。
字節流:InputStream OutputStream
字符流:Reader Writer
在這四個系統中,它們的子類,都有一個共性特點:子類名後綴都是父類名,前綴名都是這個子類的功能名稱。
public static void main(String[] args)throws IOException {//讀、寫都會發生IO異常
/*
1:創建一個字符輸出流對象,用於操作文件。該對象一建立,就必須明確數據存儲位置,是一個文件。
2:對象產生後,會在堆內存中有一個實體,同時也調用了系統底層資源,在指定的位置創建了一個存儲數據的文件。
3:如果指定位置,出現了同名文件,文件會被覆蓋。
*/
FileWriterfw = new FileWriter("demo.txt"); // FileNotFoundException
/*
調用Writer類中的write方法寫入字符串。字符串並未直接寫入到目的地中,而是寫入到了流中,(其實是寫入到內存緩衝區中)。怎麼把數據弄到文 件中?
*/
fw.write("abcde");
fw.flush(); // 刷新緩衝區,將緩衝區中的數據刷到目的地文件中。
fw.close();// 關閉流,其實關閉的就是java調用的系統底層資源。在關閉前,會先刷新該流。
}
close()和flush()的區別:
flush():將緩衝區的數據刷到目的地中後,流可以使用。
close():將緩衝區的數據刷到目的地中後,流就關閉了,該方法主要用於結束調用的底層資源。這個動作一定做。
io異常的處理方式:io一定要寫finally;
FileWriter寫入數據的細節:
1:window中的換行符:\r\n兩個符號組成。 linux:\n。
2:續寫數據,只要在構造函數中傳入新的參數true。
3:目錄分割符:window \\ /
public static void main(String[] args) {
FileWriter fw = null;
try {
fw= new FileWriter("demo.txt",true);
fw.write("abcde");
}
catch (IOException e ){
System.out.println(e.toString()+"....");
}
finally{
if(fw!=null)
try{
fw.close();
}
catch (IOException e){
System.out.println("close:"+e.toString());
}
}
}
FileReader:使用Reader體系,讀取一個文本文件中的數據。返回 -1 ,標誌讀到結尾。
import java.io.*;
class FileReaderDemo {
publicstatic void main(String[] args) throws IOException {
/*
創建可以讀取文本文件的流對象,FileReader讓創建好的流對象和指定的文件相關聯。
*/
FileReader fr =new FileReader("demo.txt");
int ch = 0;
while((ch =fr.read())!= -1){ //條件是沒有讀到結尾
System.out.println((char)ch); //調用讀取流的read方法,讀取一個字符。
}
fr.close();
}
}
讀取數據的第二種方式:第二種方式較爲高效,自定義緩衝區。
import java.io.*;
class FileReaderDemo2 {
publicstatic void main(String[] args) throws IOException {
FileReader fr = newFileReader("demo.txt"); //創建讀取流對象和指定文件關聯。
//因爲要使用read(char[])方法,將讀取到字符存入數組。所以要創建一個字符數組,一般數組的長度都是1024的整數倍。
char[] buf = new char[1024];
int len = 0;
while(( len=fr.read(buf)) != -1) {
System.out.println(newString(buf,0,len));
}
fr.close();
}
}
IO中的使用到了一個設計模式:裝飾設計模式。
裝飾設計模式解決:對一組類進行功能的增強。
包裝:寫一個類(包裝類)對被包裝對象進行包裝;
1、包裝類和被包裝對象要實現同樣的接口;
2、包裝類要持有一個被包裝對象;
3、包裝類在實現接口時,大部分方法是靠調用被包裝對象來實現的,對於需要修改的方法我們自己實現;
字符流:
Reader:用於讀取字符流的抽象類。子類必須實現的方法只有 read(char[],int, int) 和 close()。
|---BufferedReader:從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。可以指定緩衝區的大小,或者可使用默認的大小。大多數情況下,默認值就足夠大了。
|---LineNumberReader:跟蹤行號的緩衝字符輸入流。此類定義了方法setLineNumber(int)和getLineNumber(),它們可分別用於設置和獲取當前行號。
|---InputStreamReader:是字節流通向字符流的橋樑:它使用指定的charset讀取字節並將其解碼爲字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。
|---FileReader:用來讀取字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩衝區大小都是適當的。要自己指定這些值,可以先在 FileInputStream 上構造一個 InputStreamReader。
|---CharArrayReader:
|---StringReader:
Writer:寫入字符流的抽象類。子類必須實現的方法僅有 write(char[],int, int)、flush()和 close()。
|---BufferedWriter:將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入。
|---OutputStreamWriter:是字符流通向字節流的橋樑:可使用指定的charset將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺默認的字符集。
|---FileWriter:用來寫入字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩衝區大小都是可接受的。要自己指定這些值,可以先在 FileOutputStream 上構造一個 OutputStreamWriter。
|---PrintWriter:
|---CharArrayWriter:
|---StringWriter:
字節流:
InputStream:是表示字節輸入流的所有類的超類。
|--- FileInputStream:從文件系統中的某個文件中獲得輸入字節。哪些文件可用取決於主機環境。FileInputStream用於讀取諸如圖像數據之類的原始字節流。要讀取字符流,請考慮使用 FileReader。
|---FilterInputStream:包含其他一些輸入流,它將這些流用作其基本數據源,它可以直接傳輸數據或提供一些額外的功能。
|--- BufferedInputStream:該類實現緩衝的輸入流。
|--- Stream:
|---ObjectInputStream:
|---PipedInputStream:
OutputStream:此抽象類是表示輸出字節流的所有類的超類。
|---FileOutputStream:文件輸出流是用於將數據寫入File
或FileDescriptor
的輸出流。
|--- FilterOutputStream:此類是過濾輸出流的所有類的超類。
|---BufferedOutputStream:該類實現緩衝的輸出流。
|--- PrintStream:
|--- DataOutputStream:
|--- ObjectOutputStream:
|--- PipedOutputStream:
緩衝區是提高效率用的,給誰提高呢?
BufferedWriter:是給字符輸出流提高效率用的,那就意味着,緩衝區對象建立時,必須要先有流對象。明確要提高具體的流對象的效率。
FileWriter fw = newFileWriter("bufdemo.txt");
BufferedWriter bufw =new BufferedWriter(fw);//讓緩衝區和指定流相關聯。
for(int x=0; x<4;x++){
bufw.write(x+"abc");
bufw.newLine();//寫入一個換行符,這個換行符可以依據平臺的不同寫入不同的換行符。
bufw.flush();//對緩衝區進行刷新,可以讓數據到目的地中。
}
bufw.close();//關閉緩衝區,其實就是在關閉具體的流。
BufferedReader:
FileReader fr = newFileReader("bufdemo.txt");
BufferedReaderbufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){ //readLine方法返回的時候是不帶換行符的。
System.out.println(line);
}
bufr.close();
//記住,只要一讀取鍵盤錄入,就用這句話。
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(System.out));//輸出到控制檯
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//將輸入的字符轉成大寫字符輸出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
流對象:其實很簡單,就是讀取和寫入。但是因爲功能的不同,流的體系中提供N多的對象。那麼開始時,到底該用哪個對象更爲合適呢?這就需要明確流的操作規律。
流的操作規律:
1,明確源和目的。
數據源:就是需要讀取,可以使用兩個體系:InputStream、Reader;
數據匯:就是需要寫入,可以使用兩個體系:OutputStream、Writer;
2,操作的數據是否是純文本數據?
如果是:數據源:Reader
數據匯:Writer
如果不是:數據源:InputStream
數據匯:OutputStream
3,雖然確定了一個體系,但是該體系中有太多的對象,到底用哪個呢?
明確操作的數據設備。
數據源對應的設備:硬盤(File),內存(數組),鍵盤(System.in)
數據匯對應的設備:硬盤(File),內存(數組),控制檯(System.out)。
4,需要在基本操作上附加其他功能嗎?比如緩衝。
如果需要就進行裝飾。
轉換流特有功能:轉換流可以將字節轉成字符,原因在於,將獲取到的字節通過查編碼表獲取到指定對應字符。
轉換流的最強功能就是基於字節流 +編碼表。沒有轉換,沒有字符流。
發現轉換流有一個子類就是操作文件的字符流對象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操作文本文件,必須要進行編碼轉換,而編碼轉換動作轉換流都完成了。所以操作文件的流對象只要繼承自轉換流就可以讀取一個字符了。
但是子類有一個侷限性,就是子類中使用的編碼是固定的,是本機默認的編碼表,對於簡體中文版的系統默認碼錶是GBK。
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"),"gbk");
以上兩句代碼功能一致,
如果僅僅使用平臺默認碼錶,就使用FileReader fr = new FileReader("a.txt"); //因爲簡化。
如果需要制定碼錶,必須用轉換流。
轉換流 =字節流+編碼表。
轉換流的子類File =字節流 +默認編碼表。
凡是操作設備上的文本數據,涉及編碼轉換,必須使用轉換流。