1.IO流用來解決設備間的數據傳輸問題。
2.IO流的分類
按流的方向:
輸入流:讀取數據
輸出流:寫出數據
按數據類型:
字節流
字節輸入流:讀取數據 InputStream
字節輸出流:寫出數據 OutputSteam
字符流
字符輸入流:讀取數據 Reader
字符輸出流:寫出數據 Writer
什麼情況下使用哪種流呢?
如果數據所在的文件通過Windows自帶的文件夾可以打開並且能夠讀懂裏面的內容,就用字符流。其他用字節流
如果什麼都不知道,就用字節流。
注意:InputSteam和OutputSteam都是抽象類(abseract),不能直接用,需要使用它們具體的子類。
其實現類主要有以下:
InputStream:FileInputStream, FilterInputStream,StringBufferInputStream 等
OutputStream: FileOutputStream, FilterOutputStream 等
注意:每種基類的子類都是以父類名作爲後綴名
3. 字節輸出流FileOutputSteam
構造函數:
public void FileOutputStream(File file) :創建一個指向該file文件的字節輸出流對象
public void FileOutputStream(String name) :創建一個指向該路徑的字節輸出流對象
public void FileOutputStream(File file, boolean append) :創建一個指向該file文件的字節輸出流對象,如果append值爲true,則將字節寫到文件末尾處,否則會覆蓋原來的。
public void FileOutputStream(String name, boolean append) :創建一個指向該路徑的字節輸出流對象,如果append值爲true,則將字節寫到文件末尾處,否則會覆蓋原來的。
寫數據方法:
public void write(byte[] b) :寫一個字節
public void write(int b) :寫一個字節數組
public void write(byte[] b, int off, int len) :寫一個字節數組的一部分
使用步驟:
A:創建字節輸出流對象
FileOutputSteam fos = new FileOutputSteam(“fos.txt”) ;
此處字節輸出流做了幾件事情:
1)如果不存在該文件,則調用系統功能去創建該文件,如果存在則不做這件事
2)創建fos對象
3)把fos指向這個文件
B:寫數據
fos.write( “Hello.IO”.getBytes() ) ;
此處用的方法是 void write(byte[] b) ; 可見參數是一個字節數組,因此要把字符串轉換爲字節數組。
C:釋放資源
fos.close() ;
爲什麼一定要close()呢?
1)讓流對象變成垃圾,這樣就可以被垃圾回收器回收
2)通知系統去釋放跟該文件相關的資源(這點很重要)
問題1:如何在寫入的時候實現換行?
很簡單,只需要在寫入字節流的時候同時寫入換行符即可。但是不同系統的換行符是不同的,
Windows:\r\n
Linux:\n
Mac:\n
而一些常見的高級記事本是可以識別任意換行符的。
使用IO流的過程中需要注意的事會需要加入異常處理,因此上述代碼的最終完美版應該事下面這樣:
FileOutputStream fos = null ; //這裏不賦值爲null的話close()方法會報錯
try{
fos = new FileOutputStream("fos.txt") ; //如果是絕對路徑,這裏可能會出現找不到路徑的情況,即FileNotFoundException異常
fos.write("hello.IO".getBytes()); //這裏可能會報IOException的異常
}catch(FileNotFoundException e){
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{ //爲了防止上面兩步出錯導致字節流無法運行close()方法,把關閉字節流方法寫到finally中即可。
if(fos != null) //如果第一步找不到路徑的話fos就會爲空,那麼對象調用close()方法就會無意義,因此爲了防止這種情況,加入一個判斷
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
4、字節輸入流FileInputStream
構造方法
public FileInputStream(File file) :
public FileInputStream(String name) :
讀取方法:
int read():一次讀取一個字節,讀到文件結尾的話返回-1
int read(byte[] b):一次讀取一個字節數組,返回的結果是實際讀取到的數據的長度,讀到文件結尾則返回-1
讀取步驟:
A:創建字節輸入流對象
FileInputStream fis = new FileInputStream (“fis.txt”) ;
B:調用read()方法讀取數據,並返回該字節對應的int值
int b = fis.read() ;
C:釋放資源
fis.close() ;
因爲read()方法一次指讀取一個字節,所以要用循環判斷來讀取。如下:
int b = 0 ;
while(int b = fis.read() != -1){
System.out.print((char) b) ;
}
注意:這裏只能讀取數字和英文字符,不能讀取中文漢字,因爲漢子佔用兩個字節,這樣讀取到的漢子都是亂碼,因此纔會出現字符流。
在計算機中中文存儲用的兩個字節,其中
第一個字節肯定是負數,
第二個字節常見的是負數,可能有負數,但是沒影響。
但是,實際用中並不常用read()來進行單字節讀取,通常是使用read(byte[] b)方法,這樣的效率會比單字節讀取速度提升字節數組的長度倍(理論上)。其最終版代碼如下:
FileInputStream fis = new FileInputStream("fis.txt") ;
byte[] by = new byte[1024] ; //這裏的字節數組長度通常設置爲1024或者1024的倍數,理論上速度提高1024倍
int len = 0 ;
while( (len = fis.read(by))!= -1 ){
System.out.print(new String(by , 0 , len)); //這裏一定要用0-len的長度輸出,防止最後一次讀取只能更新字節數組的前面一部分。
}
fis.close() ;
5. 由上我們可以看到設置一個數組作爲緩衝區確實可以提高讀寫效率
既然這樣的的話,Java在開始設計的時候也採用的這樣的思想,提供了一種帶緩衝區的字節類。這種類被稱爲緩衝區類(高效類)。
寫數據:BufferOutputStream
構造方法:
BufferOutputStream(OutputStream out) :創建一個緩衝輸出流,把數據寫入指定的底層輸出流
BufferOutputStream(OutputStream out , int size):創建一個指定大小的緩衝輸出流,把數據寫入指定的底層輸出流
注意:爲什麼不直接指定具體的文件或者路徑而是指定一個輸出流對象呢?
因爲字符緩衝流僅僅提供緩衝區,爲高效設計的,但是真正的讀寫還是要靠基本的流對象來實現。
BufferOutputStream bos = new BufferOutputStream(new FileOutputStream("bos.txt")) ; //通常採用匿名對象方法
bos.write("hello".getBytes());
bos.close() ;
讀數據:BufferInputStream
構造方法:
BufferInputStream(InputStream in) :創建一個緩衝輸入流
BufferInputStream(InputStream in , int size):創建一個指定大小的緩衝輸入流
BufferInputStream bis= new BufferInputStream(new BufferInputStream("bis.txt")) ;
byte[] by = new byte[1024] ; //這裏的字節數組長度通常設置爲1024或者1024的倍數,理論上速度提高1024倍
int len = 0 ;
while( (len = bis.read(by))!= -1 ){
System.out.print(new String(by , 0 , len)); //這裏一定要用0-len的長度輸出,防止最後一次讀取只能更新字節數組的前面一部分。
}
fis.close() ;