Java IO深入
IO體系
Java IO 體系種類繁多,感覺很復(fù)雜,但其實是 IO 涉及的因素太多了。在進行介紹的時候添加了設(shè)計模式等的使用,會讓你感覺更加難以理解難以使用這些IO類,在此對java的IO做了一個詳細的總結(jié)。
IO 類設(shè)計出來,肯定是為了解決 IO 相關(guān)的操作的,想一想哪里會有 IO 操作?網(wǎng)絡(luò)、磁盤。網(wǎng)絡(luò)操作相關(guān)的類是在 java.net 包下,不在本文的總結(jié)范圍內(nèi)。提到磁盤,你可能會想到文件,文件操作在 IO 中是比較典型的操作。在 Java 中引入了 “流” 的概念,它表示任何有能力產(chǎn)生數(shù)據(jù)源或有能力接收數(shù)據(jù)源的對象。數(shù)據(jù)源可以想象成水源,海水、河水、湖水、一杯水等等。數(shù)據(jù)傳輸可以想象為水的運輸,古代有用桶運水,用竹管運水的,現(xiàn)在有鋼管運水,不同的運輸方式對應(yīng)不同的運輸特性。
從數(shù)據(jù)來源或者說是操作對象角度看,IO 類可以分為:
1、文件(file):FileInputStream、FileOutputStream、FileReader、FileWriter
2、數(shù)組([]):
2.1、字節(jié)數(shù)組(byte[]):ByteArrayInputStream、ByteArrayOutputStream2.2、字符數(shù)組(char[]):CharArrayReader、CharArrayWriter3、管道操作:PipedInputStream、PipedOutputStream、PipedReader、PipedWriter
4、基本數(shù)據(jù)類型:DataInputStream、DataOutputStream
5、緩沖操作:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
6、打印:PrintStream、PrintWriter
7、對象序列化反序列化:ObjectInputStream、ObjectOutputStream
8、轉(zhuǎn)換:InputStreamReader、OutputStreWriter
數(shù)據(jù)源節(jié)點也可以再進行二次處理,使數(shù)據(jù)更加容易使用,所以還可以劃分成節(jié)點流和處理流,涉及到設(shè)計模式的使用。
 
從數(shù)據(jù)傳輸方式或者說是運輸方式角度看,可以將 IO 類分為:
1、字節(jié)流
 2、字符流
字節(jié)流是以一個字節(jié)單位來運輸?shù)?#xff0c;比如一杯一杯的取水。而字符流是以多個字節(jié)來運輸?shù)?#xff0c;比如一桶一桶的取水,一桶水又可以分為幾杯水。
字節(jié)流和字符流的區(qū)別:
 字節(jié)流讀取單個字節(jié),字符流讀取單個字符(一個字符根據(jù)編碼的不同,對應(yīng)的字節(jié)也不同,如 UTF-8 編碼是 3 個字節(jié),中文編碼是 2 個字節(jié)。)字節(jié)流用來處理二進制文件(圖片、MP3、視頻文件),字符流用來處理文本文件(可以看做是特殊的二進制文件,使用了某種編碼,人可以閱讀)。簡而言之,字節(jié)是個計算機看的,字符才是給人看的。
字節(jié)流和字符流的劃分可以看下面這張圖。
 
 不可否認,Java IO 相關(guān)的類確實很多,但我們并不是所有的類都會用到,我們常用的也就是文件相關(guān)的幾個類,如文件最基本的讀寫類 File 開頭的、文件讀寫帶緩沖區(qū)的類 Buffered 開頭的類,對象序列化反序列化相關(guān)的類 Object 開頭的類。
IO類和相關(guān)方法
IO 類雖然很多,但最基本的是 4 個抽象類:InputStream、OutputStream、Reader、Writer。最基本的方法也就是一個讀 read() 方法、一個寫 write() 方法。方法具體的實現(xiàn)還是要看繼承這 4 個抽象類的子類,畢竟我們平時使用的也是子類對象。這些類中的一些方法都是(Native)本地方法、所以并沒有 Java 源代碼,下面我對這些常用類進行了總結(jié)。
先來看 InputStream 和 OutStream 中的方法簡介,因為都是抽象類、大都是抽象方法、所以就不貼源碼嘍!注意這里的讀取和寫入,其實就是獲取(輸入)數(shù)據(jù)和輸出數(shù)據(jù)。
InputStream 類
讀取數(shù)據(jù)
public abstract int read()將讀取到的數(shù)據(jù)放在 byte 數(shù)組中,該方法實際上是根據(jù)下面的方法實現(xiàn)的,off 為 0,len 為數(shù)組的長度
public int read(byte b[])從第 off 位置讀取 len 長度字節(jié)的數(shù)據(jù)放到 byte 數(shù)組中,流是以 -1 來判斷是否讀取結(jié)束的(注意這里讀取的雖然是一個字節(jié),但是返回的卻是 int 類型 4 個字節(jié))
public int read(byte b[], int off, int len)跳過指定個數(shù)的字節(jié)不讀取,想想看電影跳過片頭片尾
public long skip(long n)返回可讀的字節(jié)數(shù)量
public int available()讀取完,關(guān)閉流,釋放資源
public void close()標記讀取位置,下次還可以從這里開始讀取,使用前要看當前流是否支持,可以使用 markSupport() 方法判斷
public synchronized void mark(int readlimit)重置讀取位置為上次 mark 標記的位置
public synchronized void reset()判斷當前流是否支持標記流,和上面兩個方法配套使用
public boolean markSupported()OutputStream 類
寫入一個字節(jié),可以看到這里的參數(shù)是一個 int 類型,對應(yīng)上面的讀方法,int 類型的 32 位,只有低 8 位才寫入,高 24 位將舍棄。
public abstract void write(int b)將數(shù)組中的所有字節(jié)寫入,和上面對應(yīng)的 read() 方法類似,實際調(diào)用的也是下面的方法。
public void write(byte b[])將 byte 數(shù)組從 off 位置開始,len 長度的字節(jié)寫入
public void write(byte b[], int off, int len)強制刷新,將緩沖中的數(shù)據(jù)寫入
public void flush()關(guān)閉輸出流,流被關(guān)閉后就不能再輸出數(shù)據(jù)了
public void close()再來看 Reader 和 Writer 類中的方法,你會發(fā)現(xiàn)和上面兩個抽象基類中的方法很像。
Reader 類
讀取字節(jié)到字符緩存中
public int read(java.nio.CharBuffer target)讀取單個字符
public int read()讀取字符到指定的 char 數(shù)組中
public int read(char cbuf[])從 off 位置讀取 len 長度的字符到 char 數(shù)組中
abstract public int read(char cbuf[], int off, int len)跳過指定長度的字符數(shù)量
public long skip(long n)和上面的 available() 方法類似
public boolean ready()判斷當前流是否支持標記流
public boolean markSupported()標記讀取位置,下次還可以從這里開始讀取,使用前要看當前流是否支持,可以使用 markSupport() 方法判斷
public void mark(int readAheadLimit)重置讀取位置為上次 mark 標記的位置
public void reset()關(guān)閉流釋放相關(guān)資源
abstract public void close()Writer 類
寫入一個字符
public void write(int c)寫入一個字符數(shù)組
public void write(char cbuf[])從字符數(shù)組的 off 位置寫入 len 數(shù)量的字符
abstract public void write(char cbuf[], int off, int len)寫入一個字符串
public void write(String str)從字符串的 off 位置寫入 len 數(shù)量的字符
public void write(String str, int off, int len)追加吸入一個字符序列
public Writer append(CharSequence csq)追加寫入一個字符序列的一部分,從 start 位置開始,end 位置結(jié)束
public Writer append(CharSequence csq, int start, int end)追加寫入一個 16 位的字符
public Writer append(char c)強制刷新,將緩沖中的數(shù)據(jù)寫入
abstract public void flush()關(guān)閉輸出流,流被關(guān)閉后就不能再輸出數(shù)據(jù)了
abstract public void close()下面我們就直接使用他們的子類,在使用中再介紹下面沒有的新方法。
1、讀取控制臺中的輸入
import java.io.*;public class IOTest {public static void main(String[] args) throws IOException {// 三個測試方法 // test01(); // test02();test03();}public static void test01() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("請輸入一個字符");char c;c = (char) bufferedReader.read();System.out.println("你輸入的字符為"+c);}public static void test02() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("請輸入一個字符,按 q 鍵結(jié)束");char c;do {c = (char) bufferedReader.read();System.out.println("你輸入的字符為"+c);} while (c != 'q');}public static void test03() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("請輸入一行字符");String str = bufferedReader.readLine();System.out.println("你輸入的字符為" + str);} }至于控制臺的輸出,我們其實一直都在使用呢,System.out.println() ,out 其實是 PrintStream 類對象的引用,PrintStream 類中當然也有 write() 方法,但是我們更常用 print() 方法和 println() 方法,因為這兩個方法可以輸出的內(nèi)容種類更多,比如一個打印一個對象,實際調(diào)用的對象的 toString() 方法。
2、二進制文件的寫入和讀取
 注意這里文件的路徑,可以根據(jù)自己情況改一下,雖然這里的文件后綴是txt,但該文件卻是一個二進制文件,并不能直接查看。
3、文本文件的寫入和讀取
 write() 方法和 append() 方法并不是像方法名那樣,一個是覆蓋內(nèi)容,一個是追加內(nèi)容,append() 內(nèi)部也是 write() 方法實現(xiàn)的,也非說區(qū)別,也就是 append() 方法可以直接寫 null,而 write() 方法需要把 null 當成一個字符串寫入,所以兩者并無本質(zhì)的區(qū)別。需要注意的是這里并沒有指定文件編碼,可能會出現(xiàn)亂碼的問題。
使用字節(jié)流和字符流的轉(zhuǎn)換類 InputStreamReader 和 OutputStreamWriter 可以指定文件的編碼,使用 Buffer 相關(guān)的類來讀取文件的每一行。
@Testpublic void test10() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream(new File("").getAbsolutePath()+"/io/test2.txt");OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "GBK"); // 使用 GBK 編碼文件outputStreamWriter.write("Hello,world!\n歡迎進入Java IO\n");outputStreamWriter.append("另外一行內(nèi)容");outputStreamWriter.flush();System.out.println("文件的編碼為" + outputStreamWriter.getEncoding());outputStreamWriter.close();fileOutputStream.close();}@Testpublic void test11() throws IOException {FileInputStream fileInputStream = new FileInputStream(new File("").getAbsolutePath()+"/io/test2.txt");InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "GBK"); // 使用 GBK 解碼文件BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str;while ((str = bufferedReader.readLine()) != null) {System.out.println(str);}bufferedReader.close();inputStreamReader.close();}4、復(fù)制文件
 我進行了一些測試,不使用緩沖對文件復(fù)制時間的影響,文件的復(fù)制實質(zhì)還是文件的讀寫。緩沖流是處理流,是對節(jié)點流的裝飾。
結(jié)論:Java IO 類很多,但是把握住整個體系,掌握關(guān)鍵的方法,學習起來就會輕松很多,看完這篇文章,你是否覺得 Java IO 并沒有你想的那么難呢?
總結(jié)
 
                            
                        - 上一篇: java自学笔记_JAVA自学笔记(4)
- 下一篇: Spring Boot EasyPoi导
