Java I/O相關內容學習(附執行個體和詳解)
一、Java I/O類結構以及流的基本概念
在閱讀Java I/O的執行個體之前我們必須清楚一些概念,我們先看看Java I/O的類結構圖:
Java I/O主要以流的形式進行讀寫資料。
流是一組有順序的,有起點和終點的位元組集合,是對資料轉送的總稱或抽象。即資料在兩裝置間的傳輸稱為流,流的本質是資料轉送,根據資料轉送特性將流抽象為各種類,方便更直觀的進行資料操作。
根據處理資料的資料類型的不同可以分為:字元流和位元組流。
字元流和位元組流的主要區別:
1.位元組流讀取的時候,讀到一個位元組就返回一個位元組; 字元流使用了位元組流讀到一個或多個位元組(中文對應的位元組數是兩個,在UTF-8碼錶中是3個位元組)時。先去查指定的編碼錶,將查到的字元返回。
2.位元組流可以處理所有類型資料,如:圖片,MP3,AVI視頻檔案,而字元流只能處理字元資料。只要是處理純文字資料,就要優先考慮使用字元流,除此之外都用位元組流。
3.實際上位元組流在操作時本身不會用到緩衝區(記憶體),是檔案本身直接操作的,而字元流在操作時使用了緩衝區,通過緩衝區再操作檔案。
下面我們以檔案操作作為執行個體進一步瞭解。
二、字元流執行個體
之前提到過“只要是處理純文字資料,就要優先考慮使用字元流,除此之外都用位元組流”。因此本字元流操作執行個體是操作txt檔案。對其進行讀寫操作。
2.1、一些概念
此前,我們需要瞭解一些概念。
Java採用16位的Unicode來表示字串和字元的。在寫入字元流時我們都可以指定寫入的字串的編碼。
這裡博主貼出字元流類圖結構,方便猿友閱讀:
在檔案操作的時候我們主要使用到FileReader和FileWriter或BufferedReader和BufferedWriter。<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrTTwOC94bm5zbzAtL+0o7o8L3A+DQo8cD5GaWxlUmVhZGVyysdJbnB1dFN0cmVhbVJlYWRlcrXE19PA4KOstvhJbnB1dFN0cmVhbVJlYWRlcsrHUmVhZGVytcTX08Dgo7s8L3A+DQo8cD5GaWxlV3JpdGVyysdPdXRwdXRTdHJlYW1Xcml0ZXK1xNfTwOCjrLb4T3V0cHV0U3RyZWFtV3JpdGVy1PLKx1dyaXRlcrXE19PA4KGjPC9wPg0KPHA+PHN0cm9uZz4yLjKhokZpbGVSZWFkZXK6zUJ1ZmZlcmVkUmVhZGVytcTKudPDPC9zdHJvbmc+PC9wPg0KPHA+PHN0cm9uZz5GaWxlUmVhZGVytcSzo9PDubnU7LD8wKjS1M/Cwb3W1qO6PC9zdHJvbmc+PC9wPg0KPHA+o6gxo6lGaWxlUmVhZGVyKFN0cmluZyBmaWxlTmFtZSmjurj5vt3OxLz+w/u0tL2oRmlsZVJlYWRlcrbUz/OhozwvcD4NCjxwPqOoMqOpRmlsZVJlYWRlcihGaWxlIGZpbGUpo7q4+b7dRmlsZbbUz/O0tL2oRmlsZVJlYWRlcrbUz/OhozwvcD4NCjxwPjxzdHJvbmc+RmlsZVJlYWRlcrXEs6PTw7e9t6iw/MCo0tTPwry41tajujwvc3Ryb25nPjwvcD4NCjxwPqOoMaOpaW50IHJlYWQoKaO6tsHIobWluPbX1rf7oaO3tbvY19a3+7XE1fvK/da1o6zI57n70tG+rbW9tO/OxLz+zrKjrNTyt7W72C0xLjwvcD4NCjxwPqOoMqOpaW50IHJlYWQoY2hhcltdIGNidWYpo7q9q9fWt/u2wcjrY2J1ZtfWt/vK/dfpoaO3tbvYtsHIobW9tcTX1rf7yv2jrMjnufvS0b6ttb20787EvP7OsqOs1PK3tbvYLTEuPC9wPg0KPHA+o6gzo6lpbnQgcmVhZChjaGFyW10gY2J1ZixpbnQgb2ZmLGludCBsZW4po7q9q7bByKG1vbXE19a3+7Tmt8W1vWNidWbX1rf7yv3X6bTTb2ZmserKtrXExqvSxs671sO/qsq8tKajrNfutuC2wcihbGVuuPbX1rf7oaM8L3A+DQo8cD48c3Ryb25nPkJ1ZmZlcmVkUmVhZGVy09DS1M/Cwb3W1rm51Oy3vbeoo7o8L3N0cm9uZz48L3A+DQo8cD6jqDGjqUJ1ZmZlcmVkUmVhZGVyKFJlYWRlciBpbik6uPm+3WlutPqx7bXEUmVhZGVyttTP87S0vahCdWZmZXJSZWFkZXLKtcD9o6y7urPlx/i089ChssnTw8SsyM/WtaGjPC9wPg0KPHA+o6gyo6lCdWZmZXJlZFJlYWRlcihSZWFkZXIgaW4saW50IHN6KTq4+b7daW60+rHttcRSZWFkZXK21M/ztLS9qEJ1ZmZlcmVkUmVhZGVyyrXA/aOsu7qz5cf4tPPQobLJ08PWuLaoc3rWtaGjPC9wPg0KPHA+PHN0cm9uZz5CdWZmZXJlZFJlYWRlcrXEs6PTw7e9t6iw/MCo0tTPwry41tajujwvc3Ryb25nPjwvcD4NCjxwPqOoMaOpaW50IHJlYWQoKaO6t7W72NfWt/u1xNX7yv3WtaOsyOe5+9LRvq21vbTvzsS8/s6yo6zU8re1u9gtMS48L3A+DQo8cD6jqDKjqWludCByZWFkKGNoYXJbXSwgaW50LCBpbnQpo7q9q7bByKG1vbXE19a3+7Tmt8W1vWNidWbX1rf7yv3X6bTTb2ZmserKtrXExqvSxs671sO/qsq8tKajrNfutuC2wcihbGVuuPbX1rf7oaM8L3A+DQo8cD6jqDOjqVN0cmluZyByZWFkTGluZSgpo7q2wcih0rvOxLG+0NCho7jDt723qNP2tb3S1M/C19a3+7vy1d/X1rf7tK7Iz86qtbHHsNDQveHK+KO6JmxzcXVvO1xuJnJzcXVvOyi7u9DQt/spLCZyc3F1bztcciZyc3F1bzsou9iztbf7KSwmcnNxdW87XHJcbiZyc3F1bzsou9iztbu70NApoaO3tbvY1rXOqrjD0NDE2sjdtcTX1rf7tK6jrLK7sPy6rMjOus7Q0NbV1rm3+6OsyOe5+9LRtb2078H3xKnOsqOs1PK3tbvYbnVsbKGjPC9wPg0KPHA+PHN0cm9uZz60+sLryrXA/aO6PC9zdHJvbmc+PC9wPg0KPHByZSBjbGFzcz0="brush:java;">package java_io;import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class TestReader { public static void main(String[] args) { TestReader testReader = new TestReader(); String path = "C:\\Users\\luoguohui\\Desktop\\readerTest.txt"; testReader.readFileByFileReader(path); testReader.readFileByBufferedReader(path); } public void readFileByFileReader(String path){ FileReader fileReader = null; try { fileReader = new FileReader(path); char[] buf = new char[1024]; //每次讀取1024個字元 int temp = 0; System.out.println("readFileByFileReader執行結果:"); while ((temp = fileReader.read(buf)) != -1) { System.out.print(new String(buf, 0, temp)); } System.out.println(); } catch (Exception e) { e.printStackTrace(); } finally { //像這種i/o操作盡量finally確保關閉 if (fileReader!=null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void readFileByBufferedReader(String path){ File file = new File(path); if (file.isFile()) { BufferedReader bufferedReader = null; FileReader fileReader = null; try { fileReader = new FileReader(file); bufferedReader = new BufferedReader(fileReader); String line = bufferedReader.readLine(); System.out.println("readFileByBufferReader執行結果:"); while (line != null) { System.out.println(line); line = bufferedReader.readLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileReader.close(); bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
上面代碼用到finally,關於finally雖然與I/O無關,不過這裡還是說一下:
1、不管有木有出現異常,finally塊中代碼都會執行;
2、當try和catch中有return時,finally仍然會執行;
3、finally是在return後面的運算式運算後執行的(此時並沒有返回運算後的值,而是先把要返回的值儲存起來,管finally中的代碼怎麼樣,返回的值都不會改變,任然是之前儲存的值),所以函數傳回值是在finally執行前確定的;
4、finally中最好不要包含return,否則程式會提前退出,傳回值不是try或catch中儲存的傳回值。
readerTest.txt常值內容:
執行結果:
2.3、FileWriter和BufferWriter的使用
FileWriter的常用構造有以下四種:
(1)FileWriter(String fileName):根據檔案名稱建立FileWriter對象。
(2)FileWriter(String fileName,boolean append):根據檔案名稱建立FileWriter對象,append參數用來指定是否在原檔案之後追加內容。
(3)FileWriter(File file):根據File對象建立FileWriter對象。
(4)FileWriter(File file,boolean append):根據File對象建立FileWriter對象,append參數用來指定是否在原檔案之後追加內容。
FileWriter的常用方法包括以下幾種:
(1)void writer(int c):向檔案中寫入正整數c代表的單個字元。
(2)void writer(char[] cbuf):向檔案中寫入字元數組cbuf。
(3)void writer(char[] cbuf,int off, in len):向檔案中寫入字元數組cbuf從位移位置off開始的len個字元。
(4)void writer(String str):向檔案中寫入字串str,注意此方法不會在寫入完畢之後自動換行。
(5)void writer(String str,int off,int len):向檔案中寫入字串str的從位置off開始、長度為len的一部分子串。
(6)Writer append(char c):向檔案中追加單個字元c。
(7)Writer append(CharSequence csq):向檔案中追加csq代表的一個字元序列。CharSequence是從JDK1.4版本開始引入的一個介面,代表字元值的一個可讀序列,此介面對許多不同種類的字元序列提供統一的唯讀訪問。
(8)Writer append(CharSequence csq,int start,int end):向檔案中追加csq字元序列的從位置start開始、end結束的一部分字元。
(9)void flush():重新整理字元輸出資料流緩衝區。
(10)void close():關閉字元輸出資料流。
BufferedWriter也擁有如下兩種形式的構造方法:
(1)BufferedWriter(Writer out): 根據out代表的Writer對象建立BufferedWriter執行個體,緩衝區大小採用預設值。
(2)BufferedWriter(Writer out,int sz):根據out代表的Writer對象建立BufferedWriter執行個體,緩衝區大小採用指定的sz值。
BufferedWriter的常用方法包括以下幾種:
(1)void close() :關閉字元輸出資料流。
(2)void flush() :重新整理字元輸出資料流緩衝區。
(3)void newLine(): 寫入文本行。
(4)void write(char[] cbuf, int offset, int count) :向檔案中寫入字元數組cbuf從位移位置off開始的len個字元。
(5)void write(int oneChar) :寫入單個字元。
(6)void write(String str, int offset, int count) :向檔案中寫入字串str的從位置off開始、長度為len的一部分子串。
(7)以上的方法都是重寫了Writer的,還有繼承自java.io.Writer 的方法:Writer append(char c)、Writer append(CharSequence csq)、Writer append(CharSequence csq, int start, int end)、void write(char[] cbuf)、write(String str)等方法。
代碼執行個體:
package java_io;import java.io.BufferedWriter;import java.io.File;import java.io.FileNotFoundException;import java.io.FileWriter;import java.io.IOException;public class TestWriter { public static void main(String[] args) { TestWriter testWriter = new TestWriter(); String path = "C:\\Users\\luoguohui\\Desktop\\readerTest.txt"; testWriter.writeFileByFileWriter(path); testWriter.writeFileByBufferWriter(path); } public void writeFileByFileWriter(String path){ FileWriter fileWriter = null; try { fileWriter = new FileWriter(path,true); //將字串寫入到流中,\r\n表示換行 //因為fileWriter不會自動換行 fileWriter.write("本行是通過fileWriter加入的行\r\n"); //如果想馬上看到寫入效果,則需要調用w.flush()方法 fileWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(fileWriter != null) { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void writeFileByBufferWriter(String path){ File file = new File(path); if (file.isFile()) { BufferedWriter bufferedWriter = null; FileWriter fileWriter = null; try { fileWriter = new FileWriter(file,true); bufferedWriter = new BufferedWriter(fileWriter); bufferedWriter.write("本行是通過bufferedWriter加入的行\r\n"); bufferedWriter.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileWriter.close(); bufferedWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
我們先把readerTest.txt檔案的內容清空,運行結果如下(不清空也行,只是運行結果博主的不一樣):
三、位元組流執行個體
3.1、執行個體之前
再次聲明之前提到過的“只要是處理純文字資料,就要優先考慮使用字元流,除此之外都用位元組流”。
這裡博主貼出位元組流類圖結構,方便猿友閱讀:
下面我們依舊以檔案讀寫為例。
3.2、FileInputStream的使用
FileInputStream的構造方法:
(1)FileInputStream(File file) :通過開啟一個到實際檔案的串連來建立一個 FileInputStream,該檔案通過檔案系統中的 File 對象 file 指定。
(2)FileInputStream(FileDescriptor fdObj) :通過使用檔案描述符 fdObj 建立一個 FileInputStream,該檔案描述符表示到檔案系統中某個實際檔案的現有串連。
(3)FileInputStream(String name) 通過開啟一個到實際檔案的串連來建立一個 FileInputStream,該檔案通過檔案系統中的路徑名 name 指定。
FileInputStream的常用方法:
(1)int available():返回下一次對此輸入資料流調用的方法可以不受阻塞地從此輸入資料流讀取(或跳過)的估計剩餘位元組數。
(2)void close():關閉此檔案輸入資料流並釋放與此流有關的所有系統資源。
(3)protected void finalize():確保在不再引用檔案輸入資料流時調用其 close 方法。
(4)FileChannel getChannel():返回與此檔案輸入資料流有關的唯一 FileChannel 對象。
(5)FileDescriptor getFD():返回表示到檔案系統中實際檔案的串連的 FileDescriptor 對象,該檔案系統正被此 FileInputStream 使用。
(6)int read():從此輸入資料流中讀取一個資料位元組。
(7)int read(byte[] b):從此輸入資料流中將最多 b.length 個位元組的資料讀入一個 byte 數組中。
(8)int read(byte[] b, int off, int len):從此輸入資料流中將最多 len 個位元組的資料讀入一個 byte 數組中。
(9)long skip(long n):從輸入資料流中跳過並丟棄 n 個位元組的資料。
代碼執行個體:
package java_io;import java.io.FileInputStream;import java.io.IOException;public class TestFileInputStream { public static void main(String[] args) { TestFileInputStream testFileInputStream = new TestFileInputStream(); String path = "C:\\Users\\luoguohui\\Desktop\\readerTest.txt"; testFileInputStream.readFileByFileInputStream(path); } public void readFileByFileInputStream(String path) { FileInputStream fileInputStream = null; try { // 建立檔案輸入資料流對象 fileInputStream = new FileInputStream(path); // 設定讀取的位元組數 int n = 1024; byte buffer[] = new byte[1024]; // 讀取輸入資料流 System.out.println("readFileByFileInputStream執行結果:"); while ((fileInputStream.read(buffer, 0, n) != -1) && (n > 0)) { System.out.print(new String(buffer)); } System.out.println(); } catch (IOException ioe) { ioe.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // 關閉輸入資料流 if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }}
readerTest.txt內容:
運行結果:
3.3、FileOutputStream 的使用
FileOutputStream的構造方法:
(1)FileOutputStream(File file) :建立一個向指定 File 對象表示的檔案中寫入資料的檔案輸出資料流。
(2)FileOutputStream(File file, boolean append) :建立一個向指定 File 對象表示的檔案中寫入資料的檔案輸出資料流。append參數用來指定是否在原檔案之後追加內容。
(3)FileOutputStream(FileDescriptor fdObj) :建立一個向指定檔案描述符處寫入資料的輸出檔案流,該檔案描述符表示一個到檔案系統中的某個實際檔案的現有串連。
(4)FileOutputStream(String name) :建立一個向具有指定名稱的檔案中寫入資料的輸出檔案流。
(5)FileOutputStream(String name, boolean append) : 建立一個向具有指定 name 的檔案中寫入資料的輸出檔案流。append參數用來指定是否在原檔案之後追加內容。
FileOutputStream的常用方法:
(1)void close() :關閉此輸出資料流並釋放與此流有關的所有系統資源。
(2)void flush() :重新整理此輸出資料流並強制寫出所有緩衝的輸出位元組。
(3)void write(byte[] b) :將 b.length 個位元組從指定的位元組數組寫入此輸出資料流。
(4)void write(byte[] b, int off, int len) :將指定位元組數組中從位移量 off 開始的 len 個位元組寫入此輸出資料流。
(6)abstract void write(int b) :將指定的位元組寫入此輸出資料流。
代碼執行個體:
package java_io;import java.io.FileOutputStream;public class TestFileOutputStream { public static void main(String[] args) { TestFileOutputStream testFileOutputStream = new TestFileOutputStream(); String path = "C:\\Users\\luoguohui\\Desktop\\readerTest.txt"; testFileOutputStream.readFileByFileOutputStream(path); } public void readFileByFileOutputStream(String path) { FileOutputStream fos = null; try { fos = new FileOutputStream(path,true); String str = "這是使用FileOutputStream添加的內容\r\n"; byte[] b = str.getBytes(); fos.write(b); fos.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { fos.close(); } catch (Exception e2) { e2.printStackTrace(); } } }}
運行結果: