javaSE<IO流>

楊#琳發表於2020-12-17

1. 基本概念

  • 流:代表任何有能力產出資料的資料來源物件或者是有能力接受資料的接收端物件。
  • 流的本質:資料傳輸時的不同特徵,將流分為不同的種類。更方便對傳輸的資料進行操作。
    資料傳輸是需要通道的,而流恰恰就是這資料傳輸的通道。
  • 輸入輸出:將電腦硬碟上的資料讀到程式中的過程為輸入。(Read、Input)
    將程式中的資料寫入到外部裝置中的過程為輸出。(Write、Output)
    在這裡插入圖片描述

2.流的分類

  • 按照編碼格式:

    位元組流:可以讀寫二進位制檔案,主要處理音訊、圖片、歌曲、位元組流,處理單元為1個位元組。

    ​ 位元組流將讀取到的位元組資料,去指定的編碼表中獲取對應文字。

    ​ InputStream和OutputStream的子類都是位元組流。

    ​ 常用類:FileInputStream、OutputStream(檔案輸入位元組流、檔案輸出位元組流)。

​ 字元流:主要處理字元或字串,字元流處理單元為2個位元組。

​ (UTF-8中漢字為三個位元組,Unicode裡面漢字為兩個位元組。)

​ Reader和Writer的子類都是字元流。

​ 常用類:FileReader、FileWriter

  • 按照資料傳輸方向:

    輸入流:往程式中讀叫輸入流。

    ​ 所有輸入流都是InputStream類或者Reader類的子類。

    輸出流:從程式中往外寫叫輸出流。

    ​ 所有輸出流都是OutputStream類或者Writer類的子類。

  • 根據封裝型別:

    節點流:如果流封裝的是某種特定的資料來源,如檔案、字串、字串陣列等,則稱為節點流。

    ​ 常用類:位元組輸入流 FileInputStream
    ​ 位元組輸出流 FileOutputStream
    ​ 字元輸入流 FileReader
    ​ 字元輸出流 FileWriter

    處理流:如果流封裝的是其他流物件。該流稱為處理流。

    ​ 處理流中提供了緩衝功能,提高了(讀寫)傳輸效率。增加了許多新的方法。

    ​ 常用類:
    緩衝位元組輸出流 BufferedOutputStream
    緩衝位元組輸入流 BufferedInputStream
    緩衝字元輸入流 BufferedReader
    緩衝字元輸出流 BufferedWriter

3.位元組流

  • 位元組流:每次讀取(寫出)一個位元組,當傳輸的資原始檔有中文時,就會出現亂碼。

    • 位元組輸入流

      父類:InputStream

      常用的位元組輸入流:FileInputStream

      子類構造方法:

      ​ FileInputStream(File file) 通過開啟與實際檔案的連線建立一個FileInputStream流物件,該檔案由file物件命名。

      ​ FileInputStream(String name) 通過開啟與實際檔案的連線建立一個FileInputStream流物件,該檔案由 name命名。

      方法:

      int read();

      ​ 讀取一個位元組並以整數的形式返回(0~255),如果返回-1已到輸入流的末尾。

      int read(byte[]);

      ​ 從輸入流中讀取至多一個陣列長度的內容,返回實際讀取的位元組數,到達檔案末尾,則返回-1。

      int read(byte[],int off,int len)throws IOException

      ​ 將一個位元組型別的陣列中的從指定位置(off)開始的len個位元組讀入到輸入流。返回實際讀取的位元組 數,到達檔案末尾,則返回-1。

      ​ 陣列稱為緩衝區陣列,大小一般取值為1024的整數倍。

      void close()throws IOException;

      ​ 關閉流釋放記憶體資源。

    • 位元組輸出流

      父類:OutputStream

      常用子類:FileOutputStream 檔案位元組輸出流

      方法:

      void write (int b) throws IOException

      ​ 向輸出流中寫入一個位元組資料,該位元組資料為引數b的低8位。

      void write (byte[] b)

      ​ 將陣列b中的內容按位元組寫入到輸出流。

      void write (byte[] b, int off, int len) throws IOException

      ​ 將一個位元組型別的陣列中的從指定位置(off)開始的len個位元組寫入到輸出流。

      ​ 陣列稱為緩衝區陣列,大小一般取值為1024的整數倍。

      void close() throws IOException

      ​ 關閉流釋放記憶體資源。

      public static void main(String[] args) throws IOException {
              //輸入流向程式中讀入資料
              FileInputStream in = new FileInputStream("E:\\demo.txt" );
              //輸出流 從程式向外輸出資料
              FileOutputStream out = new FileOutputStream("F:\\demo.txt");
              //FileInputStream read(int b)//一次讀一個位元組,當內容讀完之後返回-1
              //FileInputStream read(byte[] b)//每次從輸入流中讀入ygbyte陣列個位元組,返回的是陣列中實際裝入的位元組數量
              //FileOutStream write(int b) 一次向外寫一個位元組
      
      //        int b = 0;
      //        while((b = in.read())!= -1){
      //            out.write(b);
      //        }
              byte[] b = new byte[1024];
              int length= 0;
              while((length = in.read(b))!=-1) {
                  System.out.println(length);
                  out.write(b,0,length);
              }
              in.close();
              out.close();
          }
      
      • 通過比較 read() 與 read(byte[]) 的方法複製檔案的時間的長短,可以看出,帶緩衝區的讀寫檔案的速度快,java 上提供了專門帶緩衝區的位元組流,用以提高讀寫速度。
      • 帶緩衝區的位元組流讀取速度大於普通位元組流。
      public static void main(String[] args) throws IOException {
              /*
              節點流:直接包含的是檔案,字串等資料
              處理流:包含的是其他的流物件
              BufferedInputStream,BufferedOutputStream內部提供了一個緩衝陣列,提高讀寫效率
              */
              //輸入流 向程式中讀入資料 節點流 包含的是檔案,字串等資料
              FileInputStream in = new FileInputStream("E:\\demo.txt");
              BufferedInputStream bin = new BufferedInputStream(in);
              //輸出流 從程式向外輸出資料
              FileOutputStream out = new FileOutputStream("F:\\demo.txt");
              BufferedOutputStream bou = new BufferedOutputStream(out);
              //bin.read();
              byte[] b = new byte[1024];
              int length = 0;
              while((length = in.read(b))!=-1){
                  bou.write(b,0,length);
              }
              bin.close();
              bou.flush();//重新整理緩衝輸出流
              bou.close();
          }
      
      • 通過將緩衝處理流和的read(byte[])進行比較,說明了,帶緩衝區的位元組流 讀取速度高於位元組流。

4.字元流

  • 字元流主要處理字元或字串,字元流處理單元為2個位元組。使用字元流可以傳輸並顯示字元,否則只會複製字元對應的編碼。

  • 字元輸出流

    • 所有的字元輸出流都是Writer的子類。Writer是一個抽象類。

    • CharArrayWriter、StringWriter 是兩種基本的介質流,它們分別向Char 陣列、String 中寫入資料。PipedWriter 是向與其它執行緒共用的管道中寫入資料。

    • BufferedWriter 是一個裝飾器為Writer 提供緩衝功能。

    • PrintWriter是字元列印流。

    • OutputStreamWriter 是OutputStream 到Writer 轉換的橋樑,它的子類FileWriter 其實就是一個實現此功能的具體類(具體可以研究一SourceCode)。功能和使用和OutputStream 極其類似,後面會有它們的對應圖。

    public static void main(String[] args) throws IOException {
            //字元流,每次直接讀到一個字元,底層有轉換流InputStreamReader。
            //字元流只能讀取純文字檔案。因為讀取其他格式的檔案,轉換流不同,會讀壞檔案。
            FileReader reader = new FileReader("E:\\test1.txt");
            FileWriter writer = new FileWriter("E:\\test2.txt");
    
           /* System.out.println( reader.read());
            System.out.println(reader.read());
            System.out.println(reader.read());
            System.out.println(reader.read());//會直接輸出字元對應的unicode編碼。*/
    
            int length = 0;
            while((length = reader.read()) != -1){
                writer.write(length);
                System.out.println(length);
            }
            reader.close();
            writer.close();
        }
    
    public static void main(String[] args) throws IOException {
            //字元流,每次直接讀到一個字元,底層有轉換流InputStreamReader。
            //字元流只能讀取純文字檔案。因為讀取其他格式的檔案,轉換流不同,會讀壞檔案。
            FileReader reader = new FileReader("E:\\test1.txt");
            BufferedReader reader1 = new BufferedReader(reader);
            FileWriter writer = new FileWriter("E:\\test2.txt");
            BufferedWriter writer1 = new BufferedWriter(writer);
            char[] c = new char[1024];
            String line;
            while((line = reader1.readLine())!=null){
                writer1.write(line);
                writer1.newLine();
            }
            reader1.close();
            writer1.flush();
            writer1.close();
        }
    

5.轉換流

檔案在硬碟上是以位元組流形式儲存的,通過InputStreamReader將位元組流轉化為字元流交給程式處理,程式將處理後的位元組流通過OutputStreamWriter轉化為位元組流,儲存。

  • 何時使用轉換流

    1. 當位元組和字元之間有轉換動作時。
    2. 流操作的資料物件需要解碼、編碼時。(當使用位元組流讀取到字元時,會預設呼叫轉換流的方法。)
  • 這兩個流物件是字元體系中的成員,它們有轉換作用,本身又是字元流,所以在構造的時候需要傳入位元組流物件進來。

    InputStreamReader(InputStream in);將位元組流以字元流輸入。

    OutputStreamWriter(OutputStream out);將位元組流以字元流輸出。

6.位元組流與字元流的區別

  1. 位元組流沒有緩衝區,是直接輸出的,而字元流是輸出到緩衝區的。因此在輸出時,位元組流不呼叫colse()方法

  2. 時,資訊已經輸出了,而字元流只有在呼叫close()方法關閉緩衝區時,資訊才輸出。要想字元流在未關閉時輸

    出資訊,則需要手動呼叫flush()方法。

  3. 讀寫單位不同:位元組流以位元組(8bit)為單位,字元流以字元為單位,根據碼錶對映字元,一次可能讀多個位元組。

  4. 處理物件不同:位元組流能處理所有型別的資料(如圖片、avi等),而字元流只能處理字元型別的資料。

結論:只要是處理純文字資料,就優先考慮使用字元流。除此之外都使用位元組流。

7.列印流

  • 列印流顧名思義,只做輸出沒有輸入。

  • 其分為:位元組列印流(PrintOutputStream)、字元列印流(PrintWriter)。

  • print方法可以列印各種型別的資料。

 public static void main(String[] args) throws IOException {
       PrintWriter out = new PrintWriter("E:\\test1.html");
       out.println("<b>lalala</b>");
       out.close();
    }

8.物件輸入輸出流

  • 物件的輸入輸出流 : 主要的作用是用於寫入物件資訊與讀取物件資訊。 物件資訊一旦寫到檔案上那麼物件的資訊就可以做到持久化了。

  • 物件的輸出流: ObjectOutputStream

    物件的輸入流: ObjectInputStream

  • 要將序列化之後的物件儲存下來,需要通過物件輸出流(ObjectOutputStream)將物件狀態儲存,之後再通過物件輸入流(ObjectInputStream)將物件狀態恢復。在ObjectInputStream 中用readObject()方法可以直接讀取一個物件,ObjectOutputStream中用writeObject()方法可以直接將物件儲存到輸出流中。

public static void main(String[] args) throws IOException {
//將物件資訊通過物件輸出流寫入到檔案中長期儲存的過程叫做物件序列化。
        String s = "abc";
        FileOutputStream fileOutputStream = new FileOutputStream("E:\\test2.txt");
        ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
        out.writeObject(s);//該方法可以對引數指定的obj物件進行序列化,將得到的位元組序列寫入到目標輸出流中。
    }

9.物件序列化與反序列化

  • 物件序列化的前提

    物件的壽命通常隨著生成該物件的程式的終止而終止。 有時候,可能需要將物件的狀態儲存下來,在需要時再將物件恢復。

  • 基本概念

    物件的輸出流將指定的物件寫入到檔案的過程,就是將物件序列化的過程。

    物件的輸入流將指定序列化好的檔案讀出來的過程,就是物件反序列化的過程。

    既然物件的輸出流將物件寫入到檔案中稱之為物件的序列化,所以必須要實現Serializable介面。

    Serializable介面中沒有任何方法。當一個類宣告實現Serializable介面後,表明該類可被序列化。

  • 在類中可以生成一個編號private static final long serialVersionUID *= -5974713180104013488L;隨機生成 唯一的serialVersionUID 用來表明實現序列化類的不同版本間的相容性(如果實現介面不顯式的生成序列化版本號,類的資訊一旦改變,序列化id也會隨之改變。 transient 所修飾的屬性(敏感資訊,如密碼等)不被序列化到檔案中。)。某個類在與之對應的物件已經序列化出去後做了修改,該物件依然可以被正確反序列化。