1. 程式人生 > 實用技巧 >Java基礎 - Java核心類庫 - IO 流

Java基礎 - Java核心類庫 - IO 流

Java基礎 - Java核心類庫 - IO 流

目錄

1 IO 流

1.1 基本概念
  • I (input)輸入,O(output)輸出
1.2 基本分類

(1)按照讀寫資料的基本單位不同分類:

  • 位元組流
    • 位元組流主要指以位元組為單位進行資料讀寫的流,可以讀寫任意型別的檔案
  • 字元流
    • 字元流主要指以字元(2個位元組)為單位進行資料讀寫的流,只能讀寫文字檔案

(2)按照讀寫資料方向不同分類:

  • 輸入流
    • 輸入流主要指從檔案中讀取資料內容輸入到程式中,也就是讀檔案
  • 輸出流
    • 輸出流主要指將程式中的資料內容輸出到檔案中,也就是寫檔案

(3)按照流的角色不同進行分類:

  • 節點流
    • 節點流主要指直接和輸入輸出源對接的流
  • 處理流
    • 處理流主要指需要建立在節點流的基礎之上的流
1.3 體系結構

1.4 FileWriter 類
1.4.1 基本概念
  • java.io.FileWriter類主要用於將文字內容寫入到文字檔案
1.4.2 常用方法
(1)FileWriter(String fileName) 根據引數指定的檔名構造物件,若檔案不存在該流會自動建立新空檔案,若檔案存在該流會清空檔案中的原有內容
(2)FileWriter(String fileName, boolean append) 以追加的方式根據引數指定的檔名來構造物件,不想清除存在檔案原有內容設定為true
(3)void write(int c) 寫入單個字元,可以直接'a'表示
(4)void write(char[] cbuf, int off, int len) 將指定字元陣列中從偏移量off開始的len個字元寫入此檔案輸出流
(5)void write(char[] cbuf) 將cbuf.length個字元從指定字元陣列寫入此檔案輸出流中
(6)void flush() 重新整理流
(7)void close() 關閉流物件並釋放有關的資源,先flush重新整理再關閉
1.5 FileReader 類
1.5.1 基本概念
  • java.io.FileReader類主要用於從文字檔案讀取文字資料內容
1.5.2 常用方法
FileReader(String fileName) 根據引數指定的檔名構造物件
int read() 讀取單個字元的資料並返回,返回-1表示讀取到末尾,可以通過(char) int強轉得到字元
int read(char[] cbuf, int offset, int length) 從輸入流中將最多len個字元的資料讀入一個字元陣列中(從cbuf的offset下標寫入),返回讀取到的字元個數,返回-1表示讀取到末尾
int read(char[] cbuf) 從此輸入流中將最多 cbuf.length 個字元的資料讀入字元陣列中,返回讀取到的字元個數,返回-1表示讀取到末尾
void close() 關閉流物件並釋放有關的資源
1.6 FileOutputStream類
1.6.1 概念
  • java.io.FileOutputStream類主要用於將影象資料之類的原始位元組流寫入到輸出流中
1.6.2 常用方法
FileOutputStream(String name) 根據引數指定的檔名來構造物件
FileOutputStream(String name,boolean append) 以追加的方式根據引數指定的檔名來構造物件
void write(int b) 將指定位元組寫入此檔案輸出流
void write(byte[] b, int off, int len) 將指定位元組陣列中從偏移量off開始的len個位元組寫入此檔案輸出流
void write(byte[] b) 將 b.length 個位元組從指定位元組陣列寫入此檔案輸出流中
void flush() 重新整理此輸出流並強制寫出任何緩衝的輸出位元組
void close() 關閉流物件並釋放有關的資源
1.7 FileInputStream 類
1.7.1 基本概念
  • java.io.FileInputStream類主要用於從輸入流中以位元組流的方式讀取影象資料等
1.7.2 常用方法
FileInputStream(String name) 根據引數指定的檔案路徑名來構造物件
int read() 從輸入流中讀取單個位元組的資料並返回,返回-1表示讀取到末尾
int read(byte[] b, int off, int len) 從此輸入流中將最多len個位元組的資料讀入位元組陣列中,返回讀取到的位元組個數,返回-1表示讀取到末尾
int read(byte[] b) 從此輸入流中將最多 b.length 個位元組的資料讀入位元組陣列中,返回讀取到的位元組個數,返回-1表示讀取到末尾
void close() 關閉流物件並釋放有關的資源
int available() 獲取輸入流所關聯檔案的大小
1.8 BufferedOutputStream
1.8.1 基本概念
  • java.io.BufferedOutputStream類主要用於描述緩衝輸出流,此時不用為寫入的每個位元組呼叫底層系統
1.8.2 常用方法
BufferedOutputStream(OutputStream out) 根據引數指定的引用來構造物件,預設快取大小為8192
BufferedOutputStream(OutputStream out, int size)根據引數指定的引用和緩衝區大小來構造物件
void write(int b) 寫入單個位元組
void write(byte[] b, int off, int len) 寫入位元組陣列中的一部分資料
void write(byte[] b) 寫入引數指定的整個位元組陣列
void flush() 重新整理流
void close() 關閉流物件並釋放有關的資源
1.9 BufferedInputStream
1.9.1 基本概念
  • java.io.BufferedInputStream類主要用於描述緩衝輸入流
1.9.2 常用方法
BufferedInputStream(InputStream in) 根據引數指定的引用構造物件
BufferedInputStream(InputStream in, int size) 根據引數指定的引用和緩衝區大小構造物件
int read() 讀取單個位元組
int read(byte[] b, int off, int len) 讀取len個位元組
int read(byte[] b) 讀取b.length個位元組
void close() 關閉流物件並釋放有關的資源
1.10 檔案拷貝
1.10.1 檔案字元流實現檔案拷貝
//只能拷貝文字檔案,.read()方法每次呼叫都會讀取一個字元並向後移動,所以需要一個變數來接收讀取到的每個資料

FileReader reader = null;
        FileWriter writer = null;
        try {
            reader = new FileReader("d:/iotest/a.txt");
            writer = new FileWriter("d:/iotest/b.txt");
            int i = 0;
            while ((i=reader.read())!=-1){
                writer.write(i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != reader)
                reader.close();
                if (null != writer)
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
1.10.2 檔案位元組流實現檔案拷貝

(1)單個位元組流進行拷貝

//拷貝圖片只能使用位元組流
//缺點:單個位元組為單位進行拷貝,每讀取一個位元組後再寫入一個位元組,檔案稍大時效率比很低

FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream("d:/iotest/joker.jpg");
            outputStream = new FileOutputStream("d:/iotest/joker2.jpg");

            int i = 0;
            while ((i = inputStream.read()) != -1){
                outputStream.write(i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream)
                inputStream.close();
                if (null != outputStream)
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

(2)準備一個和檔案大小一樣的陣列一次性拷貝

// 通過available方法獲取拷貝檔案的長度,再新建一個長度足夠的陣列來接收檔案,接著將檔案讀入該陣列,並判斷是否一致,一致的化執行寫入操作
//缺點:若檔案過大時,無法申請同樣大小的記憶體,真實實體記憶體不足

FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream("d:/iotest/Leecode.mp4");
            outputStream = new FileOutputStream("d:/iotest/Leecode1.mp4");

            int in = inputStream.available();
            byte[] arr = new byte[in];

            int read = inputStream.read(arr);

            if (read == in) outputStream.write(arr);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream)
                inputStream.close();
                if (null != outputStream)
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

(3)準備大小合理的陣列分批次進行拷貝

//一般根據需要拷貝的檔案的大小來設定合理的陣列大小(為1024的倍數)
//在進行寫資料操作時需要呼叫write(arr,index,len),因為檔案可能不是正好為設定的倍數

FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream("d:/iotest/Leecode.mp4");
            outputStream = new FileOutputStream("d:/iotest/Leecode1.mp4");

            byte[] arr = new byte[1024];

            int i = 0;
            while ((i=inputStream.read(arr)) != -1){
                outputStream.write(arr,0,i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream)
                inputStream.close();
                if (null != outputStream)
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
1.10.3 緩衝流進行檔案拷貝
//新建Buffered例項時需要傳入InputStream或OutputStream的子類,如FileInputStream
//在往快取裡寫資料時也可以自定義陣列批量操作進一步加快效率
//關閉流時關閉外層流內層也會關閉

BufferedInputStream inputStream = null;
        BufferedOutputStream outputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream("d:/iotest/Leecode.mp4"));
            outputStream = new BufferedOutputStream(new FileOutputStream("d:/iotest/Leecode3.mp4"));

            byte[] arr = new byte[1024];
            int res = 0;
            while ((res = inputStream.read(arr)) != -1){
                outputStream.write(arr,0,res);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(inputStream != null)
                inputStream.close();
                if (outputStream != null)
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
1.11 BufferedWriter 類
1.11.1 基本概念
  • java.io.BufferedWriter類主要用於寫入單個字元、字元陣列以及字串到輸出流中
1.11.2 常用方法
BufferedWriter(Writer out) 根據引數指定的引用來構造物件
BufferedWriter(Writer out, int sz) 根據引數指定的引用和緩衝區大小來構造物件
void write(int c) 寫入單個字元到輸出流中
void write(char[] cbuf, int off, int len) 將字元陣列cbuf中從下標off開始的len個字元寫入輸出流中
void write(char[] cbuf) 將字串陣列cbuf中所有內容寫入輸出流中
void write(String s, int off, int len) 將引數s中下標從off開始的len個字元寫入輸出流中
void write(String str) 將引數指定的字串內容寫入輸出流中
void newLine() 用於寫入行分隔符到輸出流中,使用/r/n acs 13 10
void flush() 重新整理流
void close() 關閉流物件並釋放有關的資源
1.12 BufferedReader 類
1.12.1 基本概念
  • java.io.BufferedReader類用於從輸入流中讀取單個字元、字元陣列以及字串
1.12.2 常用方法
BufferedReader(Reader in) 根據引數指定的引用來構造物件
BufferedReader(Reader in, int sz) 根據引數指定的引用和緩衝區大小來構造物件
int read() 從輸入流讀取單個字元,讀取到末尾則返回-1,否則返回實際讀取到的字元內容
int read(char[] cbuf, int off, int len) 從輸入流中讀取len個字元放入陣列cbuf中下標從off開始的位置上,若讀取到末尾則返回-1,否則返回實際讀取到的字元個數
int read(char[] cbuf) 從輸入流中讀滿整個陣列cbuf
String readLine() 讀取一行字串並返回,返回null表示讀取到末尾
void close() 關閉流物件並釋放有關的資源
1.13 PrintStream 類
1.13.1 基本概念
  • java.io.PrintStream類主要用於更加方便地列印各種資料內容
  • System.out 的列印方法就是使用該類向控制檯進行列印
1.13.2 常用方法
PrintStream(OutputStream out) 根據引數指定的引用來構造物件
void print(String s) 用於將引數指定的字串內容打印出來
void println(String x) 用於列印字串後並終止該行
void flush() 重新整理流
void close() 用於關閉輸出流並釋放有關的資源
1.14 PrintWriter 類
1.14.1 基本概念
  • java.io.PrintWriter類主要用於將物件的格式化形式列印到文字輸出流
1.14.2 常用方法
PrintWriter(Writer out) 根據引數指定的引用來構造物件
void print(String s) 將引數指定的字串內容打印出來
void println(String x) 列印字串後並終止該行
void flush() 重新整理流
void close() 關閉流物件並釋放有關的資源
1.15 OutputStreamWriter 類
1.15.1 基本概念
  • java.io.OutputStreamWriter類主要用於實現從字元流到位元組流的轉換
1.15.2 常用方法
OutputStreamWriter(OutputStream out) 根據引數指定的引用來構造物件
OutputStreamWriter(OutputStream out, String charsetName)根據引數指定的引用和編碼構造物件
void write(String str) 將引數指定的字串寫入
void flush() 重新整理流
void close() 用於關閉輸出流並釋放有關的資源
1.16 InputStreamReader類
1.16.1 基本概念
  • java.io.InputStreamReader類主要用於實現從位元組流到字元流的轉換
1.16.2 常用方法
InputStreamReader(InputStream in) 根據引數指定的引用來構造物件
InputStreamReader(InputStream in, String charsetName) 根據引數指定的引用和編碼來構造物件
int read(char[] cbuf) 讀取字元資料到引數指定的陣列
void close() 用於關閉輸出流並釋放有關的資源
1.17 實現聊天並將內容儲存到檔案中
//使用BufferedReader類來讀取鍵盤的輸入 System.in代表鍵盤輸入
//System.in 是InputStream,要使用Reader來接收需要使用InputStreamReader來轉換,BufferedReader 的readLine 方法可以讀取輸入的一行內容
//使用PrintStream類負責將資料寫入檔案
//PrintStream(OutputStream) 可以使用print方法將內容寫入指定檔案

BufferedReader reader = null;
        PrintStream stream = null;       
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            stream = new PrintStream(new FileOutputStream("d:/iotest/c.txt"));
            while (true){
                System.out.println("請輸入聊天內容:");
                String line = reader.readLine();
                System.out.println(line);
                if (line.equals("bye")){
                    break;
                }
                stream.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null)
                    reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
1.18 DataOutputStream類
1.18.1 概念
  • java.io.DataOutputStream類主要用於以適當的方式將基本資料型別寫入輸出流中
1.18.2 常用方法
DataOutputStream(OutputStream out) 根據引數指定的引用構造物件 OutputStream類是個抽象類,實參需要傳遞子類物件
void writeInt(int v) 用於將引數指定的整數一次性寫入輸出流,優先寫入高位元組 //不同方法可以寫入所有基本資料型別
void close() 用於關閉檔案輸出流並釋放有關的資源
  • writeInt 示例
int num = 66;
DataOutputStream.writeInt(num)
最後寫入文件中的結果為   B //B前面有三個空白位置
因為預設優先寫入高位,66 為int4個位元組,0000 0000 ... 0100 0010
0000 0000 對應空白字元
1.19 DataInputStream類
1.19.1 概念
  • java.io.DataInputStream類主要用於從輸入流中讀取基本資料型別的資料
1.19.2 常用方法
DataInputStream(InputStream in)根據引數指定的引用來構造物件 InputStream類是抽象類,實參需要傳遞子類物件
int readInt() 用於從輸入流中一次性讀取一個整數資料並返回
void close() 用於關閉檔案輸出流並釋放有關的資源

//write 和 read 方法需要匹配,write/read 寫讀一個位元組,writeInt/readInt 寫讀四個位元組
1.20 ObjectInputStream類
1.20.1 概念
  • java.io.ObjectInputStream類主要用於從輸入流中一次性將物件整體讀取出來
  • 所謂反序列化主要指將有效組織的位元組序列恢復為一個物件及相關資訊的轉化過程
1.20.2 常用方法
ObjectInputStream(InputStream in) 根據引數指定的引用來構造物件
Object readObject() 主要用於從輸入流中讀取一個物件並返回 無法通過返回值來判斷是否讀取到檔案的末尾
void close() 用於關閉輸入流並釋放有關的資源
1.20.3 序列化版本號

序列化機制是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時, JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如 果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常 (InvalidCastException)

1.20.4 transient關鍵字

transient是Java語言的關鍵字,用來表示一個域不是該物件序列化的一部分。當一個物件被序列 化的時候,transient型變數的值不包括在序列化的表示中,然而非transient型的變數是被包括進 去的

1.20.5 如何寫入多個物件

當希望將多個物件寫入檔案時,通常建議將多個物件放入一個集合中,然後將集合這個整體看做一 個物件寫入輸出流中,此時只需要呼叫一次readObject方法就可以將整個集合的資料讀取出來, 從而避免了通過返回值進行是否達到檔案末尾的判斷

1.21 ObjectOutputStream類
1.21.1 概念
  • java.io.ObjectOutputStream類主要用於將一個物件的所有內容整體寫入到輸出流中
  • 只能將支援 java.io.Serializable 介面的物件寫入流中
  • 類通過實現 java.io.Serializable 介面以啟用其序列化功能
  • 所謂序列化主要指將一個物件需要儲存的相關資訊有效組織成位元組序列的轉化過程
1.21.2 常用方法
ObjectOutputStream(OutputStream out) 根據引數指定的引用來構造物件
void writeObject(Object obj) 用於將引數指定的物件整體寫入到輸出流中
void close() 用於關閉輸出流並釋放有關的資源
1.22 RandomAccessFile類
1.22.1 概念
  • java.io.RandomAccessFile類主要支援對隨機訪問檔案的讀寫操作,以前讀取都是順序讀取的,現在可以指定位值
1.22.2 常用方法
RandomAccessFile(String name, String mode) 根據引數指定的名稱和模式構造物件
r: 以只讀方式開啟
rw:開啟以便讀取和寫入
rwd:開啟以便讀取和寫入,同步檔案內容的更新
rws:開啟以便讀取和寫入,同步檔案內容和元資料的更新
int read() 讀取單個位元組的資料
void seek(long pos) 用於設定從此檔案的開頭開始測量的檔案指標偏移量
void write(int b) 將引數指定的單個位元組寫入
void close() 用於關閉流並釋放有關的資源
  • seek 傳入偏移的位值,傳入n,讀取第n個位置(假設第一個下標為0)
  • read / writer 操作後會順序後移
  • seek 指定位值後再寫入資料,會覆蓋先前的資料

2 字元編碼

2.1 編碼表

計算機只能識別二進位制資料,早期就是電訊號。為了方便計算機可以識別各個國家的文字,就需要 將各個國家的文字採用數字編號的方式進行描述並建立對應的關係表,該表就叫做編碼表

2.2 常見的編碼表
ASCII:美國標準資訊交換碼, 使用一個位元組的低7位二位進位制進行表示
ISO8859-1:拉丁碼錶,歐洲碼錶,使用一個位元組的8位二進位制進行表示
GB2312:中國的中文編碼表,最多使用兩個位元組16位二進位制為進行表示
GBK:中國的中文編碼表升級,融合了更多的中文文字元號,最多使用兩個位元組16位二進位制位表示
Unicode:國際標準碼,融合了目前人類使用的所有字元,為每個字元分配唯一的字元碼。所有的文字都用兩個位元組16位二進位制位來表示
2.3 編碼發展

面向傳輸的眾多 UTF(UCS Transfer Format)標準出現了,UTF-8就是每次8個位傳輸資料,而 UTF-16就是每次16個位。這是為傳輸而設計的編碼並使編碼無國界,這樣就可以顯示全世界上所 有文化的字元了

Unicode只是定義了一個龐大的、全球通用的字符集,併為每個字元規定了唯一確定的編號,具體 儲存成什麼樣的位元組流,取決於字元編碼方案。推薦的Unicode編碼是UTF-8和UTF-16

UTF-8:變長的編碼方式,可用1-4個位元組來表示一個字元