Java標準I/O知識體系圖:
1、I/O是什麼?
I/O 是Input/Output(輸入、輸出)的簡稱,輸入流可以理解為向內存輸入,輸出流是從內存輸出。
2、流
流是一個連續的數據流,可以從流中讀取數據,也可以往流中寫數據。流與數據源,或數據源流向的媒介相關聯。
在Java IO流中,流可以是字節流,也可以是字符流。
3、Java I/O 用途與對應的流一覽
注:粗體為節點流。藍色為轉換流(字節流轉為字符流)。
4、流的處理
流分節點流和處理流兩種。
節點流:可以從或向一個特定的地方(節點)讀寫數據。如FileInputStream、FileReader。
處理流:是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫。如BufferedReader.處理流的構造方法總是要帶一個其他的流對象做參數。一個流對象經過其他流的多次包裝,稱為流的鏈接
5、文件訪問
(1)讀取文件
如果你需要在不同端使用讀取文件,你可以根據你要讀的文件是二進制文件還是文本文件,或者根據你要處理的數據是準備採取字節方式還是字符方式,決定使用 FileInputStream 或者 FileReader。兩者支持你從文件開頭開始到文件結尾讀取一個字節或者字符,也可以將讀取的多個字節或字符,寫入到內存的字節數組或字符數組。
單字節讀取文件示例:
字節數組讀取文件示例:
單字符讀取文件示例:
字符數組讀取文件示例:
(2)寫入文件
與讀取文件類似:
如果你需要在不同端使用寫入文件,你可以根據你要寫的文件是二進制文件還是文本文件,或者根據你要處理的數據是準備採取字節方式還是字符方式,決定使用 FileOutputStream 或者 FileWriter。兩者支持你可以一次寫入一個字節或者字符到文件中,也可以直接寫入一個字節數組或者字符數據。數據按照寫入的順序存儲在文件當中。
單字節寫入文件示例:
字節數組寫入文件示例:
單字符寫入文件示例:
字符數組寫入文件示例:
(3)隨機訪問文件
如果你需要不按特定的存取順序,隨意讀取或者寫入文件,可以考慮RandomAccessFile。
void seek(long pos) 設置到此文件開頭測量到的文件指針偏移量,在該位置發生下一個讀取或寫入操作。
簡單示例:
6、管道(線程內存)
管道為同一JVM中運行的線程提供基於內存的通信機制。但是你不能利用管道在不同的JVM中的線程間通信。
在概念上,Java的管道不同於Unix/Linux系統中的管道。在Unix/Linux中,運行在不同地址空間的兩個進程可以通過管道通信。在Java中,通信的雙方應該是運行在同一進程中的不同線程。當然除了管道之外,一個JVM中不同線程之間還有許多通信的方式。實際上,線程在大多數情況下會傳遞完整的對象信息而非原始的字節數據。但是,如果你需要在線程之間傳遞字節數據,Java IO的管道是一個不錯的選擇。
當使用兩個相關聯的管道流時,務必將它們分配給不同的線程。read()方法和write()方法調用時會導致流阻塞,這意味著如果你嘗試在一個線程中同時進行讀和寫,可能會導致線程死鎖。
簡單示例:
7、序列化與ObjectInputStream、ObjectOutputStream
使用ObjectInputStream、ObjectOutputStream讀取或寫入對象,首先該對象必須實現Serializable接口,使得能夠序列化和反序列化。
簡單示例:
8、回推流:PushbackInputStream與PushbackReader
PushbackInputStream/PushbackReader 用於解析InputStream/Reader內的數據,允許你讀取字節/字符後,回推(pushback)到流中,而不破壞流。
PushbackInputStream類具有以下構造函數:
第一種形式創建的流對象允許將一個字節返回到輸入流; 第二種形式創建的流對象具有一個長度為numBytes的回推緩存,從而允許將多個字節回推到輸入流中。
提供了unread()方法,如下所示:
第一種形式回推b的低字節,這會使得後續的read()調用會把這個字節再次讀取出來。第二種形式回推buffer中的字節。第三種形式回推buffer中從offset開始的numBytes個字節。當回推緩存已滿時,如果試圖回推字節,就會拋出IOException異常。
示例:
注:PushbackInputStream對象會使得InputStream對象(用於創建PushbackInputStream對象)的mark()或reset()方法無效。對於準備使用mark()或reset()方法的任何流來說,都應當使用markSupported()方法進行檢查。
9、行數記錄:LineNumberInputStream與LineNumberReader
LineNumberInputStream與LineNumberReader提供跟蹤行號的附加功能。行是以回車符 (‘\r’)、換行符 (‘\n’) 或回車符後面緊跟換行符結尾的字節序列。在所有這三種情況下,都以單個換行符形式返回行終止字符。 行號以 0 開頭,並在 read 返回換行符時遞增 1。
使用getLineNumber()可以獲取當前讀取所在行數。
示例:
10、StreamTokenizer的使用
StreamTokenizer定義了幾種基本的常量用於標識解析過程:TT_EOF(流結尾)、TT_EOL(行結尾)、TT_NUMBER(數字符號, 0 1 2 3 4 5 6 7 8 9 . -都屬於數字語法)、TT_WORD(一個單詞)。
ttype 在調用 nextToken 方法之後,此字段將包含剛讀取的標記的類型。
nval 如果當前標記是一個數字,則此字段將包含該數字的值。
sval 如果當前標記是一個文字標記,則此字段包含一個給出該文字標記的字符的字符串。
基本方法介紹一下:
nextToken() – 從此標記生成器的輸入流中解析下一個標記。
(1)標記註釋
commenChar(int ch) – 指定某個字符為註釋字符,此字符之後直到行結尾都被stream tokenizer忽略。
slashSlashComments(boolean flag) – 如果為true,則/*與*/之間的都被認為是註釋,反之,不是。
slashStartComments(boolean flag) – 如果為true,則//之後到行結尾的所有都被認為是註釋,反之,不是。
(2)基本語義
eolIsSignificant(boolean flag) – 決定一個行結束符是否被當作一個基本的符號處理,如果是true,則被當作一個基本符號,不當作普通的分隔符,如果是false,則保持原義,即當作普通的分隔符。
lowerCaseMode(boolean flag) – 決定是否讀取一個單詞時是否轉變成小寫。
parseNumbers() – 當stream tokenizer遭遇到一個單詞為雙精度的浮點數時,會把它當作一個數字,而不是一個單詞。
resetSyntax() – 重置語法表使所有的字符都被認為是“ordinary”。
(3)指定字符語義
ordinaryChar(int ch) – 指定字符在這個tokenizer中保持原義,即只會把當前字符認為普通的字符,不會有其他的語義。
ordinaryChars(int low, int hi) – 指定範圍內的字符保持語義,同上
whitespaceChars(int low, int hi) – 字符low與hi之間的所有字符都被當作為空格符,即被認識為tokenzier的分隔符。
wordChars(int low, int hi) – 字符low與hi之間的所有字符都被當作為單詞的要素。一個單詞是由一個單詞要素後面跟著0個或者更多個單詞要素或者數字要素。
11、合併流SequenceInputStream
SequenceInputStream會將與之相連接的流集組合成一個輸入流並從第一個輸入流開始讀取,直到到達文件末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的末尾為止。 合併流的作用是將多個源合併合一個源。
閱讀更多 戚小柒說IT 的文章