Java IO通常包含兩個部分:html
1.java.io包中堵塞型IO;java
2.java.nio包中的非堵塞型IO,一般稱爲New IO。數據庫
java.io包下,分爲四大塊近80個類:數組
一、基於字節操做的I/O接口:InputStream和OutputStream緩存
二、基於字符操做的I/O接口:Writer和Reader網絡
三、基於磁盤操做的I/O接口:File多線程
四、基於網絡操做的I/O接口:Socket(不在java.io包下)dom
影響IO性能的無非就是兩大因素:數據的格式及存儲的方式,前兩類主要是數據格式方面的,後兩個類是存儲方式方面的:本地和網絡。函數
咱們不多單獨使用哪一個類來實現IO操做,平時都是幾個類合起來使用,這其實體現了一種裝飾器模式源碼分析
字節流能夠處理任意類型的數據,而字符只能處理字符類型的數據
在Java中把不一樣的輸入/輸出源抽象表述爲"流"。流是一組有順序的字節集合,是對數據傳輸的總稱或抽象。
流有輸入和輸出,輸入時是流從數據源流向程序。輸出時是流從程序傳向數據源,而數據源能夠是內存,文件,網絡或程序等。
Java採用unicode編碼,通講,2個字節來表示一個字符。
在0~127整數之間的字符映射,unicode向下兼容ASCII,也就是1個字節表示一個字符。
一箇中文或英文字符的unicode編碼都佔2個字節。
編碼方式 | 英文字符 | 中文字符 |
---|---|---|
GB 23十二、GBK | 1 | 2 |
UTF-8 | 1 | 3-4 |
UTF-16 | 2 | 3-4 |
UTF-32 | 4 | 4 |
咱們先來看看類圖:
FileInputStream:從文件中讀取數據。
ByteArrayInputStream:將內存中的Byte數組適配爲一個InputStream。
StringBufferInputStream:將內存中的字符串適配爲一個InputStream,內部實現用的是StringBuffer。
該類被Deprecated。主要緣由是StringBuffer不該該屬於字節流,因此推薦使用StringReader。
SequenceInputStream:將多個流對象轉化成一個InputStream。
PipedInputStream:用於從管道中讀取數據,在流中實現了管道的概念。
ObjectInputStream: 該流容許讀取或寫入用戶自定義的類(對象)。
FilterInputStream:裝飾器類,爲其它InputStream類提供功能。java對I/O訪問多提供的同步機制是過濾流,保證某時刻只有一個線程訪問一個I/O流。
DataInputStream:通常和DataOutputStream配對使用,完成基本數據類型的讀寫。
BufferedInputStream:使用該對象阻止每次讀取一個字節都會頻繁操做IO。將字節讀取一個緩存區,從緩存區讀取。
LineNumberInputStream:跟蹤輸入流中的行號。可調用getLineNumber和 setLineNumber(int)方法獲得和設置行號。
PushbackInputStream: 能夠在讀取最後一個byte 後將其放回到緩存中。主要用在編譯器的語法、詞法分析部分。
ByteArrayOutputStream:在內存中建立一個buffer。全部寫入此流中的數據都被放入到此buffer中。
FileOutputStream:將信息寫入文件中。
PipedOutputStream:任何寫入此對象的信息都被放入對應PipedInputStream 對象的緩存中,從而完成線程的通訊,實現了「管道」的概念。
FilterOutputStream:實現裝飾器功能的抽象類。爲其它OutputStream對象增長額外的功能。
DataOutputStream:使用它能夠寫入基本數據類型。
BufferedOutputStream:使用它能夠避免頻繁地向IO寫入數據,數據通常都寫入一個緩存區,在調用flush方法後會清空緩存、一次完成數據的寫入。
PrintStream:產生具備格式的輸出信息。
System.in, System.out, System.error(注:Java標準輸入、輸出、錯誤輸出)是PrintStream的實例。
咱們在寫入文件的時候,經常由於要保存的是一個對象,也就是一個obj,可是裏面的變量又不少,咱們不可能挨個申明,一個個寫入,這時候,咱們就可使用對象的序列化與反序列化。
序列化就是對象到保存文件的過程。
反序列化就是從保存的文件,轉換爲對象的過程。
通常在如下幾種狀況下,咱們可能會用到序列化:
a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想經過RMI傳輸對象的時候。
管道主要用來實現同一個虛擬機進程中的兩個線程進行交流。所以,一個管道既能夠做爲數據源媒介也可做爲目標媒介。
須要注意的是java中的管道和Unix/Linux中的管道含義並不同,在Unix/Linux中管道能夠做爲兩個位於不一樣空間進程通訊的媒介,而在java中,管道只能爲同一個JVM進程中的不一樣線程進行通訊。
BufferedInputStream顧名思義,就是在對流進行寫入時提供一個buffer來提升IO效率。
在進行磁盤或網絡IO時,原始的InputStream對數據讀取的過程都是一個字節一個字節操做的,而BufferedInputStream在其內部提供了一個buffer,在讀數據時,會一次讀取一大塊數據到buffer中,這樣比單字節的操做效率要高的多,特別是進程磁盤IO和對大量數據進行讀寫的時候。
使用BufferedInputStream十分簡單,只要把普通的輸入流和BufferedInputStream組合到一塊兒便可。
(1)編碼方式:而DataOutputStream則採用的是UTF-8。
(2)異常機制:DataOutputStream在經過write()向「輸出流」中寫入數據時,若產生IOException,會拋出。
(3)構造函數:DataOutputStream的構造函數只有一個:DataOutputStream(OutputStream out)。即它只支持以輸出流out做爲「DataOutputStream的輸出流」。
(4)目的:DataOutputStream的做用是裝飾其它的輸出流,它和DataInputStream配合使用:容許應用程序以與機器無關的方式從底層輸入流中讀寫java數據類型。
(1)編碼方式:PrintStream是輸出時採用的是用戶指定的編碼(建立PrintStream時指定的),若沒有指定,則採用系統默認的字符編碼。
(2)異常機制:與其餘輸出流不一樣,PrintStream 永遠不會拋出 IOException;它產生的IOException會被自身的函數所捕獲並設置錯誤標記,
用戶能夠經過 checkError() 返回錯誤標記,從而查看PrintStream內部是否產生了IOException。
(3)構造函數:在PrintStream的構造函數中,能「指定字符集」和「是否支持自動flush()操做」。所謂自動flush,就是往PrintStream寫入的數據會馬上調用flush()函數
(4)目的:PrintStream爲其它輸出流提供打印各類數據值表示形式,使其它輸出流能方便的經過print(), println()或printf()等輸出各類格式的數據。
(01) out是System.java的靜態變量。
(02) 並且out是PrintStream對象,PrintStream.java中有許多重載的println()方法。
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
將這兩句話細分,能夠劃分爲如下幾步:
第1步 FileDescriptor fd = FileDescriptor.out;
第2步 FileOutputStream fdOut = new FileOutputStream(fd);
第3步 BufferedOutputStream bufOut = new BufferedOutputStream(fdOut, 128);
第4步 PrintStream ps = new PrintStream(bufout, true);
第5步 setOut0(ps);
(01) 獲取FileDescriptor.java中的靜態成員out,out是一個FileDescriptor對象,它其實是「標準輸出(屏幕)」的標識符。
(02) 建立「標準輸出(屏幕)」對應的「文件輸出流」。
(03) 建立「文件輸出流」對應的「緩衝輸出流」。目的是爲「文件輸出流」添加「緩衝」功能。
(04) 建立「緩衝輸出流」對應的「打印輸出流」。目的是爲「緩衝輸出流」提供方便的打印接口,如print(), println(), printf();使其能方便快捷的進行打印輸出。
(05) System.java中setOut0() 執行setOut0(ps),setOut0()是一個native本地方法,就是將ps設置爲System.java的out靜態變量。
Reader是全部的輸入字符流的父類,它是一個抽象類。
CharReader、StringReader是兩種基本的介質流,它們分別將Char數組、String中讀取數據。
PipedReader是從與其它線程共用的管道中讀取數據。
BufferedReader很明顯就是一個裝飾器,它和其子類負責裝飾其它Reader對象。
FilterReader是全部自定義具體裝飾流的父類。
Writer和Reader操做的目的就是操做字符,不是字節,設計Writer和Reader的目的是國際化,使IO操做支持16位的Unicode。
對於IO操做,不論是磁盤仍是網絡,最終都是對字節的操做,而咱們平時寫的程序都是字符形式的,因此在傳輸的過程當中須要進行轉換。
在字符到字節的轉換過程當中,咱們須要用到一個類:InputStreamReader。
InputStreamReader是一個鏈接字節流和字符流的橋樑,它將字節流轉變爲字符流。
注意:inputStream和reader,outputStream與write的函數都很類似,而且每次進行了IO操做,要記得close,
由於IO資源並不屬於內存資源,並不會被GC回收,因此須要顯示的手動的回收資源。
對於輸出操做,close還會自動flush。
文件和文件夾的操做均可以用File來完成。
優勢: RandomAccessFile 能夠實現對文件的隨機讀寫,可是他並非繼承於以上4中基本虛擬類。
缺點:RandomAccessFile的方法有一個最大的侷限,就是只能讀寫文件,不能讀寫其餘IO節點。
使用場景:RandomAccessFile的一個重要使用場景就是網絡請求中的多線程下載及斷點續傳。
mode中,有4中啓動的方式:
"r" 以只讀方式打開。調用結果對象的任何 write 方法都將致使拋出 IOException。
"rw" 打開以便讀取和寫入。若是該文件尚不存在,則嘗試建立該文件。
"rws" 打開以便讀取和寫入,對於 "rw",還要求對文件的內容或元數據的每一個更新都同步寫入到底層存儲設備。
"rwd" 打開以便讀取和寫入,對於 "rw",還要求對文件內容的每一個更新都同步寫入到底層存儲設備。
注意:RandomAccessFile雖然能夠設置了偏移的方法,但他不能實現中間插入的效果,若是你須要實現文本中間插入的話,要先將後面的文件內容拷貝,而後寫入,最後在寫入的寫一行,將拷貝的東西複製回來。
FileDescriptor 是「文件描述符」。
FileDescriptor 能夠被用來表示開放文件、開放套接字等。
以FileDescriptor表示文件來講:當FileDescriptor表示某文件時,咱們能夠通俗的將FileDescriptor當作是該文件。可是,咱們不能直接經過FileDescriptor對該文件進行操做;若須要經過FileDescriptor對該文件進行操做,則須要新建立FileDescriptor對應的FileOutputStream,再對文件進行操做。
FileDescriptor的對象in, out, err介紹
(01) in -- 標準輸入(鍵盤)的描述符
(02) out -- 標準輸出(屏幕)的描述符
(03) err -- 標準錯誤輸出(屏幕)的描述符
java io系列01之 "目錄"(很好值得推薦)