IO流的讀寫操作
Java IO流的分類:
除了按照流的方向可以把流劃分為輸入流和輸出流兩類,按照流讀寫資料的基本單位把流劃分為位元組流和字元流兩類以外,還可以按照流是否直接連線實際資料來源,例如檔案、網路、位元組陣列等,將流又可以劃分為實體流和裝飾流兩大類。
其中實體流指直接連線資料來源的流類,如前面介紹的FileInputStream/FileOutputStream和FileReader和FileWriter,該類流直接實現將資料來源轉換為流物件,在實體流類中實現了流和資料來源之間的轉換,實體流類均可單獨進行使用。
而裝飾流指不直接連線資料來源,而是以其它流物件(實體流物件或裝飾流物件)為基礎建立的流類,該類流實現了將實體流中的資料進行轉換,增強流物件的讀寫能力,比較常用的有DataInputStream/DataOutputStream和BufferedReader/BufferedWriter等,裝飾流類不可以單獨使用,必須配合實體流或裝飾流進行使用。
由於裝飾流都是在已有的流物件基礎上進行建立的,所以這種建立流的方式被稱作“流的巢狀”,通過流的巢狀,可以修飾流的功能,例如使讀寫的速度增加或者提供更多的讀寫方式,方便資料格式的處理。
裝飾流不改變原來實體流物件中的資料內容,只是從實體流物件基礎上創建出的裝飾流物件相對於實體流物件進行了一些功能的增強。
1. IO體系中的子類名字尾絕大部分是父類名稱,而字首則是體現子類特有功能的名稱。
2. 輸入位元組流InputStreamIO 中輸入位元組流的繼承圖可見上圖,可以看出:
InputStream 是所有的輸入位元組流的父類,它是一個抽象類。
ByteArrayInputStream、StringBufferInputStream、FileInputStream是三種基本的介質流,
它們分別從Byte 陣列、StringBuffer、和本地檔案中讀取資料。PipedInputStream 是從與其它執行緒共用的管道中讀取資料。
ObjectInputStream 和所有FilterInputStream 的子類都是裝飾流。
3. 輸出位元組流OutputStream
IO 中輸出位元組流的繼承圖可見上圖,可以看出:
OutputStream 是所有的輸出位元組流的父類,它是一個抽象類。
ByteArrayOutputStream、FileOutputStream 是兩種基本的介質流,它們分別向Byte 陣列、和本地檔案中寫入資料。
PipedOutputStream 是向與其它執行緒共用的管道中寫入資料,
ObjectOutputStream 和所有FilterOutputStream 的子類都是裝飾流。
Reader中常見的方法:
int read() :讀取一個字元,並返回讀到的這個字元,讀到流的末尾則返回-1。
int read(char[]) :將讀到的字元存入指定的陣列中,返回的是讀到的字元個數,讀到流的末尾則返回-1。
close() :讀取字元其實用的是window系統的功能,就希望使用完畢後,進行資源的釋放。
Writer中常見的方法:
write() :將一個字元寫入到流中。
write(char[]) :將一個字元陣列寫入到流中。
writer(String): 將一個字元寫入到流中。
flush():重新整理流,將流中的資料重新整理到目的地中,流還存在。
close() :關閉資源,在關閉錢會先呼叫flush(),重新整理流中的資料到目的地。
字元流的緩衝區:
緩衝區的出現提高了對流的操作效率。 原理:其實就是將陣列進行封裝。
對應的物件 :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();
使用緩衝區對文字檔案進行拷貝程式碼:
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();
}
}
}
在進行流的關閉時,先關閉輸出流,在關閉輸入流。
位元組流:
InputStream(讀)
OutputStream(寫)
由於位元組是二進位制資料,所以位元組流可以操作任何型別的資料,值得注意的是字元流使用的是字元陣列char[]而位元組流使用的是位元組陣列byte[]。
使用位元組流讀寫文字
private static void test5(){
FileOutputStream fos=null;
try{
fos=new FileOutputStream("D:/test.txt");
fos.write(0010);//寫入二進位制資料
fos.flush();
}catch(IOException e){
}finally{
try{
fos.close();
}catch(IOException ex){
}
}
FileInputStream fis=null;
try{
fis=new FileInputStream("D:/test.txt");
//fis.available()是獲取關聯檔案的位元組數,即test.txt的位元組數
//這樣建立的陣列大小就和檔案大小剛好相等
//這樣做的缺點就是檔案過大時,可能超出jvm的記憶體空間,從而造成記憶體溢位
byte[] buf=new byte[fis.available()];
fis.read(buf);
System.out.println(new String(buf));
}catch(IOException e){
}finally{
try{
fos.close();
}catch(IOException ex){
}
}
}
轉換流:是位元組流和字元流之間的橋樑
該流物件可以對讀取到的位元組資料進行指定編碼表的編碼轉換
何時使用: 當位元組和字元之間有轉換動作時;流操作的資料需要進行編碼表的指定時。
具體物件體現 :InputStreamReader:位元組到字元的橋樑
OutputStreamWriter:字元到位元組的橋樑
建構函式:
InputStreamReader(InputStream) :通過該建構函式初始化,使用的是系統預設的編碼表GBK。
InputStreamReader(InputStream,String charset) :通過該建構函式初始化,可以通過charset引數指定編碼。
OutputStreamWriter(OutputStream):使用的是系統預設的編碼表GBK。
OutputStreamWriter(OutputSream,String charset) :通過該建構函式初始化,可以通過引數charset指定編碼。
操作檔案的字元流物件是轉換流的子類
|--Reader
|--InputStreamReader(轉換流)
|--FileReader(檔案字元流)
|--Writer
|--OutputStreamWriter(轉換流)
|--FileWriter(檔案字元流)
在使用FileReader操作文字資料時,該物件使用的是預設的編碼表,如果要使用指定的編碼表,必須使用轉換流。
操作test.txt中的資料都是使用了系統預設的編碼GBK:
InputStreamReader isr=new InputStreamReader(new FileInputStreamReader(“test.txt”));
如果test.txt中的資料是通過UTF-8形式編碼的,那麼在讀取的時候就需要指定編碼表:
isr=newInputStreamReader(new FileInputStream(“a.txt”),”UTF-8”);
由於編碼不同,多位元組的字元可能佔用多個位元組。比如GBK的漢字就佔用2個位元組,而UTF-8的漢字就佔用3個位元組。
其實所有的字元流的實質上是對位元組流的封裝,在方法的底層還是對位元組流方法的呼叫,所以任何基於位元組的操作都是正確的。無論你是文字檔案還是二進位制的檔案又或者是圖片和多媒體檔案。但是位元組流在處理純文字的檔案時,效率是不如位元組流的,所以如果確認流裡面只有可列印的字元,包括英文的和各種國家的文字,也包括中文,那麼可以考慮用字元流。