JAVA學習第十一天之io流

學習目的

  1. 了解java流的概念
  2. 了解java對流的分類
  3. 掌握java流的對象創(chuàng)建,以及常用方法的使用
  4. 掌握java流常見的應用場景
  5. 掌握重點流:FileStream、PrintStream、ObjectStream
  6. 了解序列化與反序列化的概念和使用

I/O流

  1. 概念
    流 在java中指的是文件的轉(zhuǎn)移過程,通常是指文件在計算機硬盤和計算機內(nèi)存之間的傳遞方式。該傳遞過程包括了文件數(shù)據(jù)和方向。

  2. 字節(jié)流繼承結(jié)構(gòu)圖


    InputStream 和 OutputStream 繼承結(jié)構(gòu)圖.png
  3. 字符流繼承結(jié)構(gòu)圖


    Reader 和 Writer 繼承結(jié)構(gòu)圖.png
  4. 流與文件路徑
    輸入流對文件的讀取和輸出流對文件的寫入,都需要提前知道文件的路徑在哪,一般使用相對路徑和絕對路徑來存儲文件。

  • 相對路徑:在IDEA或eclipse中,相對路徑是項目/工程的根目錄,即項目所在的路徑就是相對路徑。項目工程所在的根目錄才是相對路徑起點,注意項目底下的子模塊路徑不是相對路徑的起點;
  • 絕對路徑:絕對路徑是任意盤符下所保存的文件路徑,引用絕對路徑的文件時,必須將絕對路徑從盤符開始具體寫明。

1、 IO流與Properties

  1. .properties文件
    .properties為后綴名的文件是java中的屬性配置文件,通常存儲一些可改變的配置信息。該文件的存儲內(nèi)容與其后綴名一樣,采用properties集合存儲key=value,即采用Map的鍵值對形式存儲,且properties的鍵與值得類型只能為String類型。在properties文件中允許key重復,但key重復時,會覆蓋value值。
  2. .properties文件原由
    在開發(fā)中,經(jīng)常遇到改變頻繁的數(shù)據(jù),因此可以將這些數(shù)據(jù)單獨寫到一個配置文件中,使用程序動態(tài)讀取。將來只需要修改這個文件的數(shù)據(jù)內(nèi)容,java代碼不需要改動,不需要重新編譯,服務器也不需要重啟,就可以拿到動態(tài)的信息。
  3. IO讀取.properties文件
    將.properties文件作為一個File對象,使用輸入流來讀取該文件的內(nèi)容,就可以獲取得到需要的配置信息,可以讓配置信息更加靈活多變,且不會觸及修改java源代碼(修改違背開閉原則)。
  4. 示例(假設已存在對應的.properties文件)
//Properties是一個Map集合,key和value都是String類型
//將XXX.properties文件中的數(shù)據(jù)讀取,就是加載到Properties對象當中
        // 新建一個輸入流對象
        FileReader reader = new FileReader("路徑\\XXX.properties");
        // 新建一個Map集合(具體是Hashtable的子類Properties)
        Properties pro = new Properties();
        // 調(diào)用Properties對象的load方法將屬性配置文件中的數(shù)據(jù)加載到Map集合中
        pro.load(reader); // 文件中的數(shù)據(jù)順著管道加載到Map集合中
        // 通過Properties的key來獲取對應value
        String username = pro.getProperty("username");
        System.out.println(username);

一.字節(jié)字符流

  1. 流概念
    文件由字符或字節(jié)構(gòu)成,將文件加載到內(nèi)存 或 將內(nèi)存數(shù)據(jù)輸出到文件,需要有輸入和輸出流的支持。因此可以把輸入和輸出流分為了兩個,字節(jié)輸入 和 字符輸入,字節(jié)輸出流 和 字符輸出流。

  2. 以流的方向分類

  • 輸入流:Input,Java中 將文件或其它輸入設備的數(shù)據(jù) 從硬盤加載到內(nèi)存的過程;
  • 輸出流:Output,Java中 將內(nèi)存中的數(shù)據(jù) 保存到硬盤中的文件或其他輸出設備的過程。
輸入流與輸出流.png
  1. 以讀取文件數(shù)據(jù)的方式分類
  • 字節(jié)流Stream:以Stream結(jié)尾命名的流都是字節(jié)流,字節(jié)流一次讀取1個字節(jié)byte(即8個二進制位)。
  • 字符流Reader、Writer:以Reader或Writer 結(jié)尾命名的流都是字符流,字符流一次讀取一個字符char(兩個字節(jié)16個二進制位)。
  1. 流關系圖


    流關系圖.png
  2. 區(qū)別
  • 字節(jié)流:每次讀取一個字節(jié),使用byte[]數(shù)組存儲、讀寫多個字節(jié);
  • 字符流:每次讀取一個字符,流本身自帶并使用char[]數(shù)組存儲、讀寫多個字符。

1.1 字節(jié)輸入流<InputStream>

  1. 概念
    InputStream 就是字節(jié)輸入流,是一個抽象類。所有繼承了 InputStream 的類都是字節(jié)輸入流,InputStream是用來從文件中讀取基本數(shù)據(jù)類型值的 Java API接口。

  2. 主要子類

  • 文件輸入流<FileInputStream>:
  • 對象輸入流<ObjectInputStream>:
  • 過濾輸入流<FilterInputStream>:
  • 緩存輸入流<BufferedInputStream>:
字節(jié)輸入流.png
  1. 字段
  • int MAX_SKIP_BUFFER_SIZE = 2048:用于確定 跳過時要使用的最大緩沖區(qū)大小。
  1. 常用方法
  • int read() :從輸入流讀取下一個數(shù)據(jù)字節(jié),讀到文件末尾時返回 -1;
  • int read(byte[] b) :從輸入流中每次最多讀取b.length個字節(jié),并將讀取的數(shù)據(jù)存儲在緩沖區(qū)數(shù)組b中;
  • int read(byte[] b, int off, int len) :將輸入流中 從偏移量off開始的最多l(xiāng)en個數(shù)據(jù)字節(jié)讀入byte數(shù)組b中;
  • long skip(long n):跳過和丟棄此輸入流中數(shù)據(jù)的 n個字節(jié)(從下標0開始);
  • void close() :關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源,finally中執(zhí)行釋放。

1.2 字節(jié)輸出流<OutputStream>

  1. 概念
    OutputStream 就是字節(jié)輸出流,是一個抽象類,所有繼承了 OutputStream 的類都是字節(jié)輸出流。OuputStream是用來將 基本數(shù)據(jù)類型數(shù)據(jù)寫入文件的 Java API接口。
  2. 主要子類
  • 文件輸出流<FileOutputStream>:
  • 對象輸出流<ObjectOutputStream>:
  • 過濾輸出流<FilterOutputStream>:
  • 緩存流<BufferedOutputStream>:
  • 打印流<PrintStream>:
字節(jié)輸出流.png
  1. 主要方法
  • void write(int b):將指定的字節(jié)內(nèi)容寫入此輸出流,通過輸出流將字節(jié)數(shù)據(jù)寫到文件中;
  • void write(byte[] b) :將 b.length個字節(jié)從指定的字節(jié)數(shù)組寫入此輸出流;
  • void write(byte[] b, int off, int len) :將指定字節(jié)數(shù)組中 從偏移量off 開始的 len個字節(jié)寫入此輸出流;
  • void flush() :write()寫完后,刷新此輸出流,并強制寫出所有管道中緩沖的輸出字節(jié);
  • void close() :關閉此輸出流并釋放與此流有關的所有系統(tǒng)資源。

1.3 字符輸入流<Reader>

  1. 概念
    Reader 就是字符輸入流,是一個抽象類,所有繼承了 Reader 的類都是字符輸入流。Reader是用于讀取字符流的抽象類,子類必須實現(xiàn)其read(char[], int, int) 和 close()方法。同時,多數(shù)子類將重寫其定義的一些方法,以提供更高的效率和其他功能。

  2. 主要子類

  • 緩存輸入流<BufferedReader >:
  • 字節(jié)轉(zhuǎn)字符輸入流<InputStreamReader >:
  • 字符文件輸入流<FileReader >:
字符輸入流.png
  1. 字段
  • Object lock:用于同步 針對此流的操作的對象。
  1. 主要方法
  • Reader():無參構(gòu)造,創(chuàng)建一個新的字符輸入流對象,并同步自身;
  • Reader(Object lock):帶參構(gòu)造,創(chuàng)建一個新的字符輸入流對象,并同步給定的對象;
  • int read() :讀取單個字符,如果已讀到文件末尾,則返回 -1;
  • int read(char[] cbuf) :將輸入流讀取得到的字符數(shù)據(jù)存入char[]數(shù)組;
  • int read(char[] cbuf, int off, int len) :從數(shù)組的偏移量off開始,將len個字符讀入數(shù)組的某一部分;
  • long skip(long n):讀取某個文件時跳過n個字符,從文件下標0開始;
  • void close() :關閉該流并釋放與此流有關的所有系統(tǒng)資源。

1.4 字符輸出流<Writer>

  1. 概念
    Writer 就是字符輸出流,是一個抽象類,所有繼承了 Writer 的類都是字符輸出流。Writer是寫入字符流的抽象類,子類必須實現(xiàn)其write(char[], int, int)、flush() 和 close()方法,完成數(shù)據(jù)到文件的寫入。同時,多數(shù)子類將重寫其定義的一些方法,以提供更高的效率和其他功能。

  2. 主要子類

  • 緩存流<BufferedWriter>:
  • 字節(jié)到字符轉(zhuǎn)換流<OutputStreamWriter>:
  • 字符文件輸出流<FileWriter>:
  • 打印流<PrintWriter>:
字符輸出流.png
  1. 主要方法
  • Writer append(char c) :將指定字符追加到此 writer;
  • void write(int c) :往文件中寫入單個字符,寫入前先清空文件原內(nèi)容;
  • void write(char[] cbuf) :往文件中寫入字符數(shù)組,每次最多寫入cbuf.length個字符;
  • void write(char[] cbuf, int off, int len) :往字符數(shù)組的某一部分,從字符數(shù)組的下標off開始,寫入len個長度字符到文件中;
  • void write(String str) :往文件中寫入字符串;
  • void write(String str, int off, int len) :往文件中寫入字符串的某一部分,從字符串下標off開始,寫入len個長度字符;
  • void flush() :刷新此輸出流,將管道中的數(shù)據(jù)刷出寫入到文件中;
  • void close() :關閉此流并釋放與此流有關的所有系統(tǒng)資源,但要先執(zhí)行 flush() 刷新它。

1.5 flush()方法

flush 的含義是刷新緩沖區(qū),就是將緩存區(qū)中的數(shù)據(jù)寫到磁盤上,而不再把數(shù)據(jù)放到內(nèi)存里。在執(zhí)行 os.close()時,會默認執(zhí)行 os.flush(),編寫代碼時可以不用顯示的調(diào)用。

二、文件流

  1. 概念
    文件流是指在程序開發(fā)中,完成對文件的讀寫操作的流稱為文件流。
    該流用于讀取和寫入普通文本、圖像、音頻、視頻等格式文件。
  2. 分類
  • 文件字節(jié)輸入流<FileInputStream>
  • 文件字節(jié)輸出流<FileOutputStream>
  • 文件字符輸入流<FileReader >
  • 文件字符輸出流<FileWriter>
  1. 區(qū)別
  • 字節(jié)流:每次讀取一個字節(jié),使用byte[]數(shù)組存儲、讀寫多個字節(jié);可以讀取所有的形式文件(文本、視頻、音頻、圖片等);
  • 字符流:每次讀取一個字符,使用char[]數(shù)組存儲、讀寫多個字符;只適合于讀取普通文本文件。

2.1 文件字節(jié)輸入流<FileInputStream,掌握>

  1. 概念
    FileInputStream是從文件系統(tǒng)(硬盤)中的某個文件中 獲得輸入字節(jié)。主要按照字節(jié)的方式讀取硬盤上的文件內(nèi)容,每次讀取一個字節(jié)。FileInputStream 用于讀取諸如圖像數(shù)據(jù)之類的原始字節(jié)流。
    <文件內(nèi)容在計算機底層都是以 二進制的補碼形式存在,1字節(jié) = 8位>。
  2. 常用方法
  • FileInputStream(String name):帶參構(gòu)造方法,根據(jù)指定文件路徑創(chuàng)建字節(jié)輸入流對象;
  • int read() :從輸入流讀取文件的下一個數(shù)據(jù)字節(jié),若已到達文件末尾則返回 -1;
  • int read(byte[] b) :從 輸入流中每一次讀取 最多b.length個字節(jié),并將讀取的內(nèi)容存儲在緩沖區(qū)的byte數(shù)組中;
  • int available():每一次讀取時,剩余未讀的字節(jié)個數(shù),結(jié)合 read(byte[] b) 方法作為byte[ available() ]數(shù)組的容量 最佳,但過大時會超出byte數(shù)組的容量;
  • long skip(long n):跳過n個字節(jié)"選擇"讀取,返回讀取時實際跳過的字節(jié)數(shù);
  • void close() :關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源。
  1. FileInputStream讀取文件圖解


    FileInputStream讀取文件.png
  2. 代碼實現(xiàn)
             //創(chuàng)建文件輸入流引用
             FileInputStream fis = null;
             try {
                  //創(chuàng)建 從指定路徑讀取文件的 輸入流對象,
                  fis = new FileInputStream("c:\\test.txt");
                  int b = 0;//存儲每次讀取文件返回的字節(jié)數(shù)據(jù) 個數(shù)
                  while ((b = fis.read()) != -1) { 
                           //System.out.print(b);//輸出讀取到的單個字節(jié)                                                                                                                                
                           System.out.print((char)b);//將讀取得到的單個字節(jié) 轉(zhuǎn)換成字符輸出  
                   }                  
                   //采用read(byte[] bytes)方法讀取時,結(jié)合使用available()作為bytes數(shù)組的容量,但數(shù)據(jù)過大時不能使用
                   // byte[] bytes = new byte[fis.available()];

                   //采用字節(jié)數(shù)組存儲字節(jié)流讀取文件的數(shù)據(jù)
                   byte[] b1 = new byte[4];
                   int readcount = 0;//存儲每次讀取數(shù)組 返回的字節(jié)個數(shù)
                   //當返回讀取的字節(jié)個數(shù)不為0
                   while ((readcount = fis.read(b1)) != -1){
                           //將每一次讀取byte數(shù)組中的數(shù)據(jù)轉(zhuǎn)換為字符串輸出,每次讀取幾個就輸出幾個
                           String s1 = new String(b1,0,readcount);
                           System.out.print(s1);//
                    }
              }catch(FileNotFoundException e) {
                          e.printStackTrace();
              }catch(IOException e) {
                          e.printStackTrace();
              }finally {      //當輸入流為空時,關閉流,釋放內(nèi)存
                         try {
                                  if (fis != null){
                                      is.close();
                                  }
                         }catch(IOException e) {}
              }
  1. 注意點
  • 常常使用 fis.read() != -1判斷是否讀取到了文件末尾;
  • 使用 byte數(shù)組存儲讀取文件的數(shù)據(jù)時,想要每次讀取幾個就輸出幾個,就使用一個變量temp 接收每次讀取的個數(shù);
  • 采用字節(jié)輸入流讀取漢字文本文件時,因為每個漢字占據(jù)兩個字節(jié),而字節(jié)輸入流每次只能讀取一個字節(jié),導致每個漢字的讀取分為"不完整的"兩部分,從而產(chǎn)生亂碼。

2.2 文件字節(jié)輸出流<FileOutputStream,掌握>

  1. 概念
    文件輸出流是 將數(shù)據(jù)寫入文件的輸出流。"寫入文件"是否可用 或 能否被創(chuàng)建,取決于基礎平臺,某些平臺一次只允許一個文件流對象打開文件進行寫入。若所涉及的文件已經(jīng)打開,則此類中的構(gòu)造方法將失?。ㄒ粋€文件無法被兩個輸出流同時寫入數(shù)據(jù))。

  2. 特點
    文件字節(jié)輸出流按照 字節(jié)方式寫出文件,適用于寫入諸如圖像數(shù)據(jù)之類的原始字節(jié)的流。

  3. 經(jīng)典案例
    文件復制:首先判斷該文件是否存在。文件存在,則(采用輸入流)讀取文件,讀取后將該文件(采用輸出流)另寫一份保存到磁盤上,完成文件的復制備份。

  4. 文件字節(jié)輸出圖


    文件字節(jié)輸出流.png
  5. 常用方法

  • FileOutputStream(String name):帶參構(gòu)造,創(chuàng)建一個 向指定文件寫入數(shù)據(jù)的 字節(jié)輸出流對象,寫入時默認清空文件的所有內(nèi)容再寫入;
  • FileOutputStream(String name, boolean append):帶參構(gòu)造,創(chuàng)建一個 向指定文件寫入數(shù)據(jù)的 字節(jié)輸出流對象;第二個參數(shù)為 true,表示將字節(jié)追加寫入文件末尾處,而不是清空文件再寫入文件開始處;
  • void write(int b):輸出流將指定的字節(jié)b寫入文件,一次寫入一個長度的字節(jié);
  • void write(byte[] b) :輸出流每次將最多 b.length 個字節(jié)從指定 byte 數(shù)組寫入此文件,通常byte數(shù)組是由輸入流讀取得到的"復制內(nèi)容";
  • void writeBytes(byte b[], int off, int len, boolean append):輸出流將數(shù)組的一部分內(nèi)容寫入文件,并使用append判斷是清空原文件內(nèi)容寫入,或是在原文件的末尾追加寫入;
  • void close() :關閉此輸出流 并釋放與此流有關的所有系統(tǒng)資源。
  1. 代碼實現(xiàn)
           //創(chuàng)建文件輸出流引用
           FileOutputStream fos = null;
               try {
                    //創(chuàng)建 指定路徑寫出文件的 輸出流文件對象,若文件不存在則在該路徑新建文件,若文件存在則清空文件的內(nèi)容再寫入
                    fos = new FileOutputStream("d:\\test.txt.bak");
                    //創(chuàng)建一個輸出流對象,寫入時不清空已存在文件的內(nèi)容,而是在文件末尾追加寫入的內(nèi)容
                    // fos = new FileOutputStream("d:\\test.txt.bak",true);  

                    //寫入單個字符
                    fos.write(1);
                    //寫入字符串
                    fos.write("Hello world!");
                    //寫入char數(shù)組
                    char[] ac = {'我','是','中','國','人'};
                    fos.write(ac);
                    //寫入完成,刷新管道
                    fos.flush();
                    System.out.println("文件復制完畢!");
                 }catch(FileNotFoundException e) {
                            e.printStackTrace();
                 }catch(IOException e) {
                            e.printStackTrace();
                 }finally {
                    // 分開try,不要一起try, 一起try時,其中一個出現(xiàn)異常,可能會影響到另一個流的關閉。
                     if (fos != null) {
                          try {
                              fos.close();
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                     }
  1. 注意點
  • 對于FileOutPutStream要寫入的文件,若文件在對象創(chuàng)建前已存在,直接對已存在文件進行寫入;若文件不存在,則會在指定路徑下新建文件再寫入;
  • FileOutPutStream對象的創(chuàng)建:調(diào)用構(gòu)造器時,注意區(qū)分單參構(gòu)造和雙參構(gòu)造,單參構(gòu)造會清空寫入文件的所有內(nèi)容再將新數(shù)據(jù)寫進;雙參構(gòu)造append若是true時,則不會清空文件內(nèi)容,而在文件末尾追加寫入的數(shù)據(jù)。

2.3 文件字符輸入流<FileReader >

  1. 概念
    FileReader是用來讀取字符文件的便捷類。主要按照 字符(雙字節(jié))的方式讀取文件內(nèi)容,即每次讀取兩個字節(jié)。<底層使用兩個字節(jié)位00110101 10111001>。
  2. 文件字符輸入流圖


    文件字符輸入流.png
  3. 常用方法
  • FileReader(String fileName):帶參構(gòu)造,在指定路徑下創(chuàng)建一個字符輸入流對象,讀取文件數(shù)據(jù)(底層調(diào)用FileInputStream對象的創(chuàng)建方式);
  • int read() :從輸入流讀取單個字符,以int類型變量接收讀取的字符;
  • int read(char[] c) :從輸入流 將字符讀入數(shù)組,一次讀入最多c.length個長度字符;
  • void close() :關閉該流并釋放與此流有關的所有系統(tǒng)資源。
  1. 代碼實現(xiàn)
//創(chuàng)建字符輸入流引用
        FileReader fileReader = null;
        try {
            //創(chuàng)建實際的字符輸入流對象
            fileReader = new FileReader("");
            //讀取單個字符
            fileReader.read();
            //用于存儲 每次讀取返回的字節(jié)個數(shù)
            int readcount = 0;
            char[] ac = new char[8];
            //讀取數(shù)組
            fileReader.read(ac);
            while ((readcount = fileReader.read(ac)) !=-1){
                //讀取數(shù)組的一部分
                fileReader.read(ac, 0, readcount);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

2.4 文件字符輸出流<FileWriter>

  1. 概念
    FileWriter是用來寫入字符文件的便捷類,每一次輸出按照字符方式(雙字節(jié))寫出文件。
  2. 文件字符輸出流圖


    文件字符輸出流.png
  3. 常用方法
  • FileWriter(String fileName):帶參構(gòu)造,創(chuàng)建一個 向指定名稱的文件中寫入數(shù)據(jù)的 字符輸出流對象;
  • FileWriter(String fileName, boolean append):帶參構(gòu)造,創(chuàng)建一個 向指定名稱的文件中寫入數(shù)據(jù)的 字符輸出流對象;第二個參數(shù)為 true,表示將字節(jié)寫入文件末尾處,而不是寫入文件開始處;
  • void flush() :刷新此輸出流,將可能殘留在管道中的數(shù)據(jù)刷出到文件中(一般在關閉流之前刷新);
  • void close() :關閉此流并釋放與此流有關的所有系統(tǒng)資源,但要先執(zhí)行 flush() 刷新;
  • void write(int c) :在輸出流中寫入單個字符;
  • void write(String str) :在輸出流中寫入字符串。
  1. 代碼實現(xiàn)
       //創(chuàng)建一個文件字符輸出流引用
        FileWriter fileWriter = null;
        try {
            //創(chuàng)建實際的字符輸出流對象
            fileWriter = new FileWriter("");
            //創(chuàng)建實際的字符輸出流對象,true表示在輸出文件后面進行append追加內(nèi)容
            //fileWriter = new FileWriter("",true);

            //寫入單個字符
            fileWriter.write(1);
            //寫入字符串
            fileWriter.write("Hello world!");
            //寫入char數(shù)組
            char[] ac = {'我','是','中','國','人'};
            fileWriter.write(ac);
            //寫入完成,刷新管道
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fileWriter != null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

三、緩沖流<BufferedStream>

  1. 概念
    緩沖流主要是通過減少物理讀取次數(shù)(程序從硬盤中讀取文件的次數(shù))的流,通過將硬盤數(shù)據(jù)讀取到一個緩沖管道中,下一次直接從緩沖管道中讀取,減少內(nèi)存與硬盤的交流次數(shù)。緩沖流是為了提高效率而存在的。

  2. 分類

  • 緩沖字節(jié)輸入流<BufferedInputStream>
  • 緩沖字節(jié)輸出流<BufferedOutputStream>
  • 緩沖字符輸入流<BufferedReader>
  • 緩沖字符輸出流<BufferedWriter>
  1. 特點
  • 構(gòu)造方法節(jié)點流:緩沖流的構(gòu)造方法中需要一個節(jié)點流作為第一參數(shù),不像文件流直接使用文件路徑或文件創(chuàng)建對象,緩沖流必須使用父類的字節(jié)輸入/輸出流、字符輸入輸出流的對象作為第一參數(shù);
  • 流自帶緩沖區(qū):緩沖流中自帶有數(shù)據(jù)緩沖區(qū),無需像文件流一樣創(chuàng)建自定義byte[]數(shù)組或char[]數(shù)組存儲 字節(jié)/字符輸入流讀取文件的數(shù)據(jù);
  1. 節(jié)點流、包裝流與處理流
  • 節(jié)點流:指的是在一個流的構(gòu)造方法內(nèi)還需要另外一個流作為參數(shù),而作為參數(shù)的流就稱為節(jié)點流,在關閉流釋放資源時,只需關閉外部流,不需要關閉節(jié)點流(外部流的close()底層自動關閉節(jié)點流);
  • 包裝流/ 處理流:指的是使用節(jié)點流作為其構(gòu)造方法參數(shù)的流,實際上對文件進行操作處理的流。

3.1 緩沖字節(jié)輸入流<BufferedInputStream>

  1. 概念
    BufferedInputStream為另一個輸入流InputStream擴展功能,添加了緩沖輸入、支持 mark() 和 reset()方法的能力。在創(chuàng)建 BufferedInputStream 時,會創(chuàng)建一個內(nèi)部緩沖區(qū)數(shù)組,在讀取或跳過流中的字節(jié)時,可根據(jù)需要從包含的輸入流再次填充該內(nèi)部緩沖區(qū),一次填充多個字節(jié)。
  2. 特點
  • mark()方法 記錄輸入流中的某個點;
  • reset()方法使得在 從包含的輸入流中獲取新字節(jié) 之前,再次讀取自最后一次標記后讀取的所有字節(jié)。
  1. 字段
  • int DEFAULT_BUFFER_SIZE = 8192:默認字符緩沖區(qū)的大?。?/li>
  • int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8:該流支持的最大緩沖區(qū)大??;
  • volatile byte buf[]:存儲 從字節(jié)輸入流中讀取的數(shù)據(jù) 的內(nèi)部緩沖數(shù)組;
  • int count:比緩沖區(qū)中最后一個有效字節(jié)的索引 大1的索引;
  • int pos:緩沖區(qū)中的當前位置position;
  • int markpos = -1:最后一次調(diào)用mark()方法時 緩沖區(qū)的位置。
  1. 常用方法
  • BufferedInputStream(InputStream in):帶參構(gòu)造,創(chuàng)建一個默認緩沖區(qū)大小的字節(jié)緩存輸入流對象,底層使用輸入流 in;
  • BufferedInputStream(InputStream in, int size):帶參構(gòu)造,創(chuàng)建一個size長度的指定緩沖區(qū)大小的 字節(jié)緩存輸入流對象,底層使用輸入流 in;
  • int read():從輸入流中讀取數(shù)據(jù)的下一個字節(jié);
  • int read(byte b[], int off, int len):將輸入流中的 len個數(shù)據(jù)字節(jié)讀入byte 數(shù)組中,從數(shù)組的下標off開始存儲;
  • long skip(long n):跳過和丟棄此輸入流中數(shù)據(jù)的 n個字節(jié);
  • void mark(int readlimit):在此輸入流中標記當前的位置;
  • void close():關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源。
 //創(chuàng)建字節(jié)輸入流對象--緩沖流中的節(jié)點流
        FileInputStream fis = new FileInputStream("");
        //創(chuàng)建緩沖字節(jié)輸出流對象--參數(shù)為節(jié)點流
        BufferedInputStream bis = new BufferedInputStream(fis);
        //創(chuàng)建緩沖字節(jié)輸出流對象--參數(shù)為節(jié)點流,且緩沖流本身自帶的緩沖區(qū)大小為128字節(jié)
        BufferedInputStream biss = new BufferedInputStream(fis,128);
        //查看緩沖流本身緩沖區(qū)的大小
        bis.available();
        //讀取單個字節(jié)
        bis.read();
        byte[] b1 = new byte[32];
        //讀取字節(jié)數(shù)組
        bis.read(b1);
        int readcount = 0;
        //讀取字節(jié)數(shù)組的一部分
        bis.read(b1,0,readcount);
        //關閉流資源,釋放內(nèi)存
        bis.close();

3.2 緩沖字節(jié)輸出流<BufferedOutputStream>

  1. 概念
    BufferedOutputStream類是實現(xiàn)緩沖的輸出流。通過設置這種輸出流,應用程序可以將各個字節(jié)寫入底層輸出流InputStream中,不必針對每次字節(jié)寫入都調(diào)用底層系統(tǒng)。
  2. 字段
  • byte buf[]:存儲數(shù)據(jù)的內(nèi)部緩沖區(qū);
  • int count:緩沖區(qū)中的有效字節(jié)數(shù)。
  1. 常用方法
  • BufferedOutputStream(OutputStream out):帶參構(gòu)造,創(chuàng)建一個默認緩沖區(qū)大小為8192的 字節(jié)緩存輸出流對象,底層使用輸出流 out;
  • BufferedOutputStream(OutputStream out, int size):帶參構(gòu)造,創(chuàng)建一個指定size長度的緩沖區(qū)大小的 字節(jié)緩存輸入流對象,底層使用輸出流 out;
  • void flushBuffer():刷新此字符緩沖區(qū);
  • void write(int b):將字符數(shù)據(jù)b寫入緩沖輸出流;
  • void write(byte b[], int off, int len):將字符數(shù)組中指定len個長度的數(shù)據(jù)寫入緩沖輸出流,從數(shù)組中的下標off開始;
  • void flush():刷新此字符緩沖輸出流,將管道中的數(shù)據(jù)刷新到文件中。
//創(chuàng)建字節(jié)輸出流對象--作為緩沖流的節(jié)點流
        FileOutputStream fos = new FileOutputStream("");
        //創(chuàng)建緩沖字節(jié)流輸出對象
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //創(chuàng)建緩沖字節(jié)流輸出對象,且緩沖流自身的緩沖區(qū)大小為28字節(jié)
        //BufferedOutputStream boss = new BufferedOutputStream(fos,128);
        //寫出單個字節(jié)
        bos.write(12);
        //寫出字節(jié)數(shù)組
        byte[] b1 = new byte[128];
        bos.write(b1);
        int writecount = 0;
        //寫出字節(jié)數(shù)組的一部分
        bos.write(b1,0,writecount);
        //刷新緩沖流
        bos.flush();
        //關閉緩沖流
        bos.close();

3.3 緩沖字符輸入流<BufferedReader>

  1. 概念
    BufferedReader是從字符輸入流中讀取文本(不是直接從文件中),緩沖各個字符,從而實現(xiàn)字符、數(shù)組和行的高效讀取。 BufferedReader可以指定緩沖區(qū)的大小,或者使用默認大小。大多數(shù)情況下,默認值足夠大。
  2. 特點
    緩沖指定文件的輸入。如果沒有緩沖,每次調(diào)用 read()或 readLine()方法都會導致從文件中讀取字節(jié),再將其轉(zhuǎn)換為字符后返回,是極其低效的。
  3. 字段
  • Reader in:字符輸入流對象(節(jié)點流),用于BufferedReader構(gòu)造方法中的第一參數(shù);
  • char cb[]:流本身自帶用于存儲 從字符輸入流中讀取的數(shù)據(jù)的字符數(shù)組;
  • int defaultCharBufferSize = 8192:默認自身緩沖區(qū)的大小為8192字節(jié)。
  1. 常用方法
  • BufferedReader(Reader in):創(chuàng)建一個默認大小輸入緩沖區(qū)的緩沖字符輸入流,需要一個節(jié)點流Reader作為對象;
  • BufferedReader(Reader in, int sz):帶參構(gòu)造,創(chuàng)建一個指定sz大小輸入緩沖區(qū)的緩沖字符輸入流;
  • int read():從字符輸入流中讀取單個字符;
  • read(char cbuf[], int off, int len):將字符讀入到數(shù)組中的某一部分;
  • String readLine(): 從字符輸入流中直接讀取一個文本行,遇到指定字符時某行終止(換行 '\n'、回車 '\r' 、回車后跟著的換行),該方法本身讀取不到換行符;
  • long skip(long n):從字符輸出流中讀取時,跳過指定n個字符,從下標0開始;
  • void close():關閉該緩沖流,釋放內(nèi)存。

3.4 緩沖字符輸出流<BufferedWriter>

  1. 概念
    BufferedWriter將文本寫入字符輸出流,緩沖各個字符,從而提供單個字符、數(shù)組和字符串的高效寫入??梢灾付ň彌_區(qū)的大小,或接受默認的大小。大多數(shù)情況下,默認值足夠大。
  2. 特點
    將緩沖 PrintWriter對文件的輸出。如果沒有緩沖,每次調(diào)用 print()方法都會將字符轉(zhuǎn)換為字節(jié),再寫入到文件,是極其低效的。
  3. 字段
  • Writer out:字符輸出對象,用于BufferedWriter構(gòu)造方法中;
  • char cb[]:用于存儲 從字符輸入流中讀取的數(shù)據(jù)的 字符數(shù)組;
  • int defaultCharBufferSize = 8192:默認緩沖區(qū)大小8192字節(jié)。
  1. 常用方法
  • BufferedWriter(Writer out):帶參構(gòu)造,創(chuàng)建一個使用默認大小輸出緩沖區(qū)的緩沖字符輸出流;
  • BufferedWriter(Writer out, int sz):帶參構(gòu)造,創(chuàng)建一個使用給定大小輸出緩沖區(qū)的新緩沖字符輸出流;
  • void ensureOpen():確認該輸出流打開(正在使用可輸出);
  • void write(int c):寫入單個字符, c-是指定要寫入字符;
  • void write(char cbuf[], int off, int len):將字符數(shù)組中的某一部分寫入流,從字符數(shù)組的下標off開始,寫入len個長度字符;
  • void write(String s, int off, int len):將字符串的某一部分寫入流,從字符串的下標off開始,寫入len個長度字符;
  • void newLine():往流中寫入一個行分隔符;
  • void flush():刷新該流的緩沖,將管道中緩沖的數(shù)據(jù)刷新到文件中;
  • void flushBuffer():刷新緩沖數(shù)據(jù);
  • void close():關閉此流,關閉前要先使用flush()刷新管道中的緩沖數(shù)據(jù)。

四、轉(zhuǎn)換流

  1. 概念
    轉(zhuǎn)換流就是將一種流的方法轉(zhuǎn)換成另一種流的方式。通常是字節(jié)流轉(zhuǎn)字符流(對比基本數(shù)據(jù)類型的轉(zhuǎn)換,小轉(zhuǎn)大可以自動轉(zhuǎn),大轉(zhuǎn)小會損失精度,單字節(jié)容納不下雙字節(jié))。
  2. 主要子類
  • 字節(jié)轉(zhuǎn)字符輸入<InputStreamReader>:
  • 字節(jié)轉(zhuǎn)字符輸出<OutputStreamWriter>:
  1. 用途
    作為一個"包裝類",將字節(jié)流轉(zhuǎn)換為字符流,結(jié)合緩沖流的構(gòu)造方法中的節(jié)點流一起使用更合適。

4.1 字節(jié)轉(zhuǎn)字符輸入<InputStreamReader>

  1. 概念
    InputStreamReader就是將字節(jié)流輸入流轉(zhuǎn)換成字符輸入流,由字面理解就是InputStream → Reader。它使用指定的字符編碼讀取字節(jié) 并將其解碼為字符,使用的字符集可以由名稱指定、顯式給定,或者接受平臺默認的字符集。
  2. 特點
  3. 字段
  • StreamDecoder sd:流解碼器對象,用于處理字節(jié)轉(zhuǎn)字符期間的編碼和解碼問題。
  1. 常用方法
  • InputStreamReader(InputStream in):帶參構(gòu)造,創(chuàng)建一個使用默認字符集的 字節(jié)轉(zhuǎn)字符輸入對象,底層使用字節(jié)輸入流對象in;
  • InputStreamReader(InputStream in, String charsetName):帶參構(gòu)造,創(chuàng)建使用指定字符集的 字節(jié)轉(zhuǎn)字符輸入對象;
  • InputStreamReader(InputStream in, Charset cs):帶參構(gòu)造,創(chuàng)建使用給定字符集的 字節(jié)轉(zhuǎn)字符輸入對象;
  • InputStreamReader(InputStream in, CharsetDecoder dec):帶參構(gòu)造,創(chuàng)建使用給定字符集解碼器的 字節(jié)轉(zhuǎn)字符輸入對象;
  • boolean ready():判斷此流是否已經(jīng)準備好用于讀取,讀取前判斷再使用;
  • int read():讀取單個字符,如果已到達流的末尾,則返回 -1 ;
  • int read(char cbuf[], int offset, int length):將字符讀入到數(shù)組中的某一部分,從數(shù)組下標offset開始存儲,讀取length個長度;
  • void close():關閉該流并釋放與之關聯(lián)的所有資源。
  1. 代碼示例
        //創(chuàng)建字符輸入流
        FileReader fr = new FileReader("");
        //創(chuàng)建字節(jié)緩沖流--內(nèi)部參數(shù)為字符流對象
        BufferedReader br1 = new BufferedReader(fr);

        //創(chuàng)建字節(jié)輸入流
        FileInputStream fio = new FileInputStream("");
        //將字節(jié)輸入流fio 轉(zhuǎn)成 字符輸入流
        InputStreamReader isr = new InputStreamReader(fio);
        //創(chuàng)建字節(jié)緩沖流--內(nèi)部參數(shù)為字符流對象
        BufferedReader br = new BufferedReader(isr);

        //合并-字節(jié)流--字節(jié)轉(zhuǎn)字符流--字符緩沖流(字符流對象參數(shù))
        BufferedReader bfr = new BufferedReader(new   InputStreamReader(new FileInputStream("")));
        bfr = new BufferedReader(new InputStreamReader(System.in));
        String s = null;
        while ((s=br.readLine()) != null) {
           System.out.println(s);
           //讀取到字母 q時退出循環(huán)
                 if ("q".equals(s)) {
                       break;
                 }
         }
        if (bfr != null) {
           bfr.close();
        }

4.2 字節(jié)轉(zhuǎn)字符輸出<OutputStreamWriter>

  1. 概念
    OutputStreamWriter就是將字節(jié)流輸出流轉(zhuǎn)換成字符輸出流,由字面理解就是OutputStream → Writer。它使用指定的字符編碼將 要寫入流中的字符編碼成字節(jié),使用的字符集可以由名稱指定、顯式給定,否則接受平臺默認的字符集。
  2. 特點
  3. 字段
  • StreamDecoder sd:流解碼器對象,用于處理字節(jié)轉(zhuǎn)字符期間的編碼和解碼問題。
  1. 常用方法
  • OutputStreamWriter(OutputStream out):帶參構(gòu)造,創(chuàng)建使用默認字符編碼的 字節(jié)轉(zhuǎn)字符輸出對象;
  • OutputStreamWriter(OutputStream out, String charsetName):帶參構(gòu)造,創(chuàng)建使用指定字符集的 字節(jié)轉(zhuǎn)字符輸出對象;
  • OutputStreamWriter(OutputStream out, Charset cs):帶參構(gòu)造,
    創(chuàng)建使用給定字符集的 字節(jié)轉(zhuǎn)字符輸出對象;
  • OutputStreamWriter(OutputStream out, CharsetEncoder enc):帶參構(gòu)造, 創(chuàng)建使用給定字符集編碼器的 字節(jié)轉(zhuǎn)字符輸出對象;
  • write(int c):寫入單個字符;
  • write(char cbuf[], int off, int len):將字符數(shù)組中的某一部分寫入流,從數(shù)組的下標off開始,寫入len個長度;
  • void write(String str, int off, int len):將字符串中的某一部分寫入流,從字符串的下標off開始,寫入len個長度;
  • void flushBuffer():刷新此流緩沖區(qū);
  • void flush():刷新該流的緩沖,將管道中緩沖的數(shù)據(jù)全都寫入文件;
  • void close():關閉此流,但要先flush()刷新它。
        //創(chuàng)建字節(jié)輸出流對象
        FileOutputStream fos = new FileOutputStream("");
        //將字節(jié)輸出流--轉(zhuǎn)換--字符輸出流
        OutputStreamWriter osw = new OutputStreamWriter(fos);

        //創(chuàng)建字節(jié)輸出流對象
        FileOutputStream fous = new FileOutputStream("");
        //將字節(jié)輸出流--轉(zhuǎn)換--字符輸出流
        OutputStreamWriter os = new OutputStreamWriter(fous);
        //將字符輸出流--轉(zhuǎn)換--緩沖字符輸出流
        BufferedWriter bw = new BufferedWriter(os);

        //合并-字節(jié)輸出流--字符輸出流--緩沖字符輸出流()
        BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("")));
        buw.flush();

五、打印流

  1. 概念
    打印流即從程序到內(nèi)存輸出的流,由java程序向控制臺打印輸出,即完成對屏幕打印的重定向。
  2. 特點
    System.out 就是打印輸出,默認輸出到控制臺。而打印流 PrintStream可以重定向輸出到文件,System.out.println不再輸出到屏幕,而是輸出到指定文件。
  3. 分類
  • 打印字節(jié)流<PrintStream>
  • 打印字符流<PrintWriter>
  1. 輸出重定向原理
    System.out默認是PrintStream向控制臺輸出;
    PrintStream 構(gòu)造時,使用指定輸出路徑的輸出流對象作為參數(shù),導致PrintStream的輸出不再默認,而是向指定輸出路徑輸出,從而完成輸出重定向。但前提也需要System.setOut才可以修改輸出方向。
  • System.setOut(new PrintStream(new OutPutStream("指定路徑")));

5.1 打印字節(jié)流<PrintStream,掌握>

  1. 概念
    PrintStream構(gòu)造器底層需要一個輸出流作為參數(shù)(節(jié)點流),因此是其他輸出流的擴展,為其他輸出流添加更多功能。PrintStream默認完成由java程序向控制臺打印輸出,即完成對屏幕打印的重定向。
  2. 特點
  • PrintStream永遠不會拋出 IOException;
  • PrintStream在寫入 byte數(shù)組之后自動調(diào)用 flush()方法完成刷新;
  • 調(diào)用println()方法,表示寫入一個換行符或字節(jié) ('\n');
  • PrintStream不需要close()關閉;
  1. 特點
    System.out就是 PrintStream,System.out默認輸出到控制臺。
    而當打印流PrintStream使用 指定路徑的輸出流作為節(jié)點流參數(shù) 來構(gòu)造時,可以重定向輸出到文件,System.out.println不再輸出到屏幕,而是輸出到指定文件。
  2. 字段
  • boolean autoFlush:自動刷新標志;
  • BufferedWriter textOut:字符輸出對象;
  • OutputStreamWriter charOut字節(jié)轉(zhuǎn)字符輸出對象;
  • Formatter formatter:格式化程序,對輸出流中的數(shù)據(jù)格式化;
  1. 常用方法
  • void flush():刷新該流的緩沖,通過將所有緩沖的輸出字節(jié)寫入到底層輸出流;
  • void close():關閉流,通過刷新流然后關閉底層輸出流完成此操作;
  • void write(int b):將指定的字節(jié)b寫入此流,如果字節(jié)為新行 且啟用了自動刷新,則調(diào)用 flush 方法;
  • void write(byte[] buf,int off, int len):將byte 數(shù)組的 len長度個字節(jié)從指定下標 off開始寫入此流。如果啟用自動刷新,則調(diào)用 flush 方法;
  • void print(Object obj):打印數(shù)據(jù),按照平臺的默認字符編碼將 String.valueOf(obj)生成的字符串轉(zhuǎn)換為字節(jié),并完全以write(int)方法的方式寫入這些字節(jié);
  • void println():通過寫入行分隔符字符串終止當前行,行分隔符字符串由系統(tǒng)屬性line.separator 定義,不一定是單個換行符 '\n';
  • PrintStream format(String format, Object args):使用指定格式字符串和參數(shù) 將格式化字符串寫入此輸出流中;
  • PrintStream append(CharSequence csq):將指定字符序列csq添加到此輸出流;
  • PrintStream append(char c):將指定字符c添加到此輸出流。
  1. 代碼示例
        //創(chuàng)建字節(jié)輸出流引用
         FileOutputStream os = null;
              try {
                  System.out.println("asdfkjfd;lldffdfdrerere");//未改變輸出方向,默認向控制臺輸出
                  //創(chuàng)建具體的輸出流對象
                   os = new FileOutputStream("c:/console.txt");
                  //創(chuàng)建標準輸出流對象--采用節(jié)點流參數(shù),將指定文件輸出的方向
                   PrintStream ps = new PrintStream(os);
                   //PrintStream ps =   System.out; 
                   System.setOut(ps);//改變輸出方向
                   //System.setOut(new PrintStream(os));//合并--改變輸出方向

                   System.out.println("asdfkjfd;lldffdfdrerere");//向指定輸出流的文件console.txt輸出
              }catch(FileNotFoundException e) {
                   e.printStackTrace();
             }catch(IOException e) {
                   e.printStackTrace();
            }finally {    //關閉流資源,釋放內(nèi)存    
                   try {
                        if (os != null) {
                            os.close();
                        }
                    }catch(IOException e) {}
           }

5.2 打印字符流<PrintWriter>

  1. 概念
    打印輸入流,相當于System.in,完成 接收屏幕輸入。
  2. 特點
  3. 字段
    -Writer out:字符輸出流對象,構(gòu)造器時使用;
  • boolean autoFlush:自動刷新標志;
  • Formatter formatter:格式化對象,對輸出流中的數(shù)據(jù)格式化;
  • PrintStream psOut = null:字節(jié)打印流對象;
  • String lineSeparator:行分隔符,用于print()方法中進行行分隔的標志;
  1. 常用方法
  • PrintWriter (Writer out):帶參構(gòu)造,創(chuàng)建一個不帶自動行刷新的字符打印流對象,底層調(diào)用字節(jié)輸出流對象;
  • void flush():刷新該流的緩沖,底層重寫了Flushable接口和Writer類的方法;
  • void close():關閉該流并釋放與之關聯(lián)的所有系統(tǒng)資源;
  • void write(int c):寫入單個字符c;
  • void write(char buf[], int off, int len):寫入字符數(shù)組的某一部分,從數(shù)組的下標off開始,寫入len個長度;
  • void write(char buf[]):寫入字符數(shù)組,此方法不能從 Writer類繼承,因為它必須取消 I/O 異常;
  • void write(String s, int off, int len):寫入字符串的某一部分;
  • void newLine():往流中寫入一個行分隔符;
  • void print(Object obj):打印數(shù)據(jù),按照平臺的默認字符編碼將 String.valueOf(obj)生成的字符串轉(zhuǎn)換為字節(jié),并完全以write(int)方法的方式寫入這些字節(jié);
  • void println():通過寫入行分隔符字符串終止當前行,底層調(diào)用newLine()方法;
  • PrintWriter append(CharSequence csq):將指定字符序列csq添加到此輸出流;
  • PrintWriter append(char c):將指定字符c添加到此輸出流。

六、對象流<掌握>

  1. 概念
    對象流是可以將 Java對象 轉(zhuǎn)換成二進制進行文件操作的流,通常對象流的輸入和輸出是以字節(jié)單位進行的。
  2. 分類
  • 對象字節(jié)輸入流<ObjectInputStream>
  • 對象字節(jié)輸出流<ObjectOutputStream>
  1. 常用方法
  • writeObject(Object obj):向硬盤下的指定文件打印輸出該obj對象;
  • readObject():從硬盤指定的文件中讀取出對象到內(nèi)存中。

6.1 對象字節(jié)輸入流<ObjectInputStream,掌握>

  1. 概念
    ObjectInputStream類是對 使用了 ObjectOutputStream類寫入的基本數(shù)據(jù)和對象進行反序列化的對象輸入流,從磁盤中將對象反序列化到內(nèi)存中。
  2. 特點
  3. 字段
  1. 常用方法
  1. 代碼示例
        //創(chuàng)建文件輸入流引用
        FileInputStream fis = null;
        //創(chuàng)建對象輸入流引用
        ObjectInputStream ois = null;
            //創(chuàng)建實際的文件輸入流對象--作為節(jié)點流參數(shù)
            fis = new FileInputStream("students");
            //創(chuàng)建實際的對象輸入流對象--完成反序列化的流
            ois = new ObjectInputStream(fis);
            //將文件中的對象反序列化到內(nèi)存中--返回的是Object類型對象
            Object o = ois.readObject();
            //將反序列化的對象進行類型強轉(zhuǎn)
            Student st = (Student)o;
            //獲取反序列對象的各個屬性
            System.out.println(st.getName());
            System.out.println(st.getAge());
            if (ois != null){
                    ois.close();
            }
            if (fis != null){
                    fis.close();
            }
        }

6.2 對象字節(jié)輸出流<ObjectOutputStream,掌握>

  1. 概念
    ObjectOutputStream是一個用于將java對象分段寫入(序列化)到硬盤中的對象輸出流。
  2. 特點
  • static class Caches:靜態(tài)內(nèi)部類--緩存,使用HashMap存儲數(shù)據(jù),使用ReferenceQueue參考隊列存儲消息。
  1. 字段
  1. 常用方法
  1. 代碼示例
//創(chuàng)建需要進行序列化的java對象
        Student stu = new Student("zhangsan",23);
        //創(chuàng)建文件輸出流引用--作為節(jié)點參數(shù)
        FileOutputStream fos = null;
        //創(chuàng)建對象輸出流引用--將java序列化寫出的流
        ObjectOutputStream oos = null;

            //創(chuàng)建實際的文件輸出流對象--指定輸出路徑
            fos = new FileOutputStream("stu");
            //創(chuàng)建實際的對象輸出流對象,使用節(jié)點流參數(shù)
            oos = new ObjectOutputStream(fos);
            //將java對象序列化寫出
            oos.writeObject(stu);
            //刷新輸出流
            oos.flush();
            //關閉流資源            
            if (fos != null){
                    fos.close();
            }
            if (oos != null){
                    oos.close();
            }
        }

6.3 序列化與反序列化<Serilizable接口>

  1. 概念
    Serilizable接口是一個序列化接口,是標記接口,該接口內(nèi)沒有任何方法。實現(xiàn)該接口只是作為一個標記給JVM看,然后JVM認為實現(xiàn)了該接口的類型對象可以進行序列化,因此該類的所有屬性和方法都會自動序列化,不必關心具體序列化的過程。
  2. 圖解


    對象序列化與反序列化圖解.png
  3. 前提
    需要進行序列化或反序列化的對象必須實現(xiàn)Serilizable接口。
//必須實現(xiàn)Serilizable接口作為JVM認可的序列化標記
public class Student implements Serializable {
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //提供get()和set()方法,并重寫toString()方法
}

6.3.1 序列化

  1. 概念
    對象流將 Java對象轉(zhuǎn)換成二進制 寫入磁盤的過程叫做序列化,底層采用輸出流作為節(jié)點流參數(shù)。
  2. 特點
    每一個java對象都會是較大的文件,因此序列化時,對象輸出流會將Java對象進行切割分段,再寫入到硬盤文件當中。
  3. 代碼示例
    //創(chuàng)建需要序列化的java對象
        Student stu = new Student("haha",21);
        //創(chuàng)建對象輸出流引用--序列化使用的寫出流
        ObjectOutputStream obj = null;
        try {
            //創(chuàng)建實際的對象輸出流對象--采用文件輸出流作為參數(shù),指定序列化路徑
            obj = new ObjectOutputStream(new FileOutputStream("students"));
            //將需要序列化的java對象寫出序列化到硬盤中
            obj.writeObject(stu);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (obj != null){
                try {
                    obj.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
  1. 注意點
  • 序列化多個對象:當需要同時序列化多個對象時,提前將多個對象存儲到數(shù)組或集合當中<建議使用泛型>,在序列化時ObjectOutputStream.writeObject(集合對象)即可一次序列化多個對象。采用集合序列化多個對象到硬盤中的文件,存儲的也是一個集合對象,而不是散開的單個泛型對象。
  • 若想采用每次序列化單個對象的方式逐次序列化多個對象,那么會在序列化到第二個對象時出現(xiàn)錯誤。

6.3.2 反序列化

  1. 概念
    對象流從磁盤讀出完整 Java 對象到內(nèi)存中的過程叫做反序列化,底層采用輸入流作為節(jié)點流參數(shù)。
  2. 代碼示例
        //創(chuàng)建反序列化的對象輸入流引用
        ObjectInputStream ois = null;
              try {
                    //創(chuàng)建實際的對象輸入流對象,使用文件輸入流作為參數(shù)
                    ois = new ObjectInputStream( new FileInputStream("c:/Person.dat"));
                    //反序列化,將對象從磁盤讀出,并轉(zhuǎn)化為對應類型的對象
                    Person person = (Person)ois.readObject();
                    //獲取反序列化后對象的屬性                    
                    System.out.println(person.name);
              }catch(ClassNotFoundException e) {
                    e.printStackTrace();
              }catch(FileNotFoundException e) {
                    e.printStackTrace();
              }catch(IOException e) {
                    e.printStackTrace();
              }finally {        //關閉流,釋放資源
                    try {
                        if (ois != null) {
                                 ois.close();
                        }
                    }catch(IOException e) {}
          }
  1. 注意點
    反序列化多個對象:若序列化文件中的對象是集合對象(即使集合里面存儲了多個泛型對象),那么反序列化時 ObjectInputStream.readObject()讀取返回的obj也是一個集合對象,因此返回時可以采用集合引用<建議使用泛型>來接收返回的集合對象。再對返回的集合對象進行迭代遍歷和獲取集合元素(單個對象)的信息。

6.4 transient關鍵字

  1. 概念
    transient意為游離的。在一個需要序列化的類中,明確有些屬性需要序列化,而其他屬性不需要被序列化時,使用transient關鍵字修飾該屬性將不會被序列化。
  2. 作用
    只需要實現(xiàn)Serilizable接口,將不需要序列化的屬性添加關鍵字transient修飾,序列化對象的時候,該屬性就不會序列化。
  3. 使用場景
    如一個用戶類有一些敏感信息<密碼,銀行卡號>,為了安全起見,不希望在網(wǎng)絡操作中被傳輸(被序列化,包括本地序列化緩存)。因此在該屬性上加上 transient關鍵字,以免被序列化。即transient修飾的字段的生命周期僅存于調(diào)用者的內(nèi)存中,而不會寫到磁盤里持久化。
class Person implements Serializable{
         String name;
         transient String password;//采用 transient 關鍵字修飾此屬性,序列化時會忽略<即不會被序列化>
}

6.5 serialVersionUID 屬性

  1. 概念
    serialVersionUID 意為序列化版本號,序列化的對象在存儲時都有一個標識版本號,使用該屬性修飾的類保證了序列化版本號唯一。
  2. 特點
  • serialVersionUID屬性使用public static final修飾;
  • 若不使用序列化版本號,在多次對原類修改添加后進行序列化時,會產(chǎn)生多個版本不一樣的對象,反序列化時也會不清楚到底反序列化哪一個版本;
  • 實現(xiàn)Serilizable接口時,系統(tǒng)默認提供序列化版本號,但是強烈建議程序員自定義類時手動添加寫上。
  1. 作用
  • java驗證類唯一性的標志之一:java的序列化機制是通過判斷類的序列化ID號來驗證 序列化對象版本一致性的;
  • 保證序列化與反序列化的文件相同且唯一:擁有serialVersionUID屬性修飾的類,即使其實例對象在進行一次序列化,之后無論對該對象進行多少次添加或修改,重新對"新對象"序列化時,得到的還是同一個對象;
  • 防止序列化兼容問題:解決序列化時對象不唯一問題,對象修改后也不出現(xiàn)序列化的版本問題,而是擁有統(tǒng)一的版本號。
  1. 圖解serialVersionUID


    serialVersionUID圖解.png
  2. 使用方式
        class Person implements Serializable{
                //加入版本號,防止序列化兼容問題,解決序列化時不一致
                private static final long serialVersionUID = -11111111L;
                String name;
                int age;
                boolean sex;
        }
  1. IDEA生成序列化版本號
    File --> Settings --> Editor --> Inspections -->搜索serializable --> Serializable Class without serialVersionUID --> 打鉤 --> ok --> 對實現(xiàn)了Serializable接口的類 atl+回車 --> Add serialVersionUID

七、數(shù)據(jù)流<DataStream>

  1. 概念
    數(shù)據(jù)流既是數(shù)據(jù)專屬的流,用于讀取文件中的數(shù)據(jù)及其數(shù)據(jù)類型,或用于寫出數(shù)據(jù)及其數(shù)據(jù)類型,數(shù)據(jù)流讀取保存或?qū)懭肷傻奈募皇瞧胀ㄎ谋疚募荒苤苯哟蜷_。
  2. 分類
  • DataOutputStream:數(shù)據(jù)輸出流,可以將數(shù)據(jù)連同數(shù)據(jù)的類型一并寫入文件,寫出的文件不是普通文本文件;
  • DataInputStream:數(shù)據(jù)輸入流,對于DataOutputStream寫出的文件,只能使用DataInputStream流去讀,并且讀取時需要提前知道寫入時的數(shù)據(jù)順序。讀的順序需要和寫的順序一致。才可以正常取出數(shù)據(jù)。
  1. 作用特點
  • 安全性:使用DataOutputStream寫出的數(shù)據(jù)連帶其數(shù)據(jù)類型,更加安全易讀;
  • 加密性:對于DataOutputStream寫出的文件必須要DataInputStream流去讀,否則亂碼,更加安全加密。
  1. DataOutputStream
        FileOutputStream fos = new FileOutputStream("data");
       // 創(chuàng)建數(shù)據(jù)專屬的字節(jié)輸出流--使用節(jié)點流
       DataOutputStream dos = new DataOutputStream(fos);
       //合并--
       //DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        // 寫數(shù)據(jù)
        byte b = 100;
        short s = 200;
        int i = 300;
        long l = 400L;
        float f = 3.0F;
        double d = 3.14;
        boolean sex = false;
        char c = 'a';
         // 把數(shù)據(jù)以及數(shù)據(jù)的類型一并寫入到文件當中。
        dos.writeByte(b);
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(sex);
        dos.writeChar(c);
        // 寫出完成時,刷新管道
        dos.flush();
        // 關閉外層流(包裝流)
        dos.close();
  1. DataInputStream
        FileInputStream fis = new FileInputStream("data");
       // 創(chuàng)建數(shù)據(jù)專屬的字節(jié)輸入流--使用節(jié)點流
       DataInputStream dis = new DataInputStream(fis);
       //合并--
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));
        // 開始讀取DataOutputStream寫出的文件--前提知道寫出的數(shù)據(jù)順序
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        boolean sex = dis.readBoolean();
        char c = dis.readChar();
        //關閉流資源              
        dis.close();

八、File 類

  1. 概念
    File 類是文件和目錄路徑名的抽象表示形式,指的是一類可以直接對文件進行操作的類(不管是硬盤上的還是內(nèi)存緩存中的)。
  2. 特點
    File類不是具體的文件,只是一條路徑,可以是目錄也可以是具體文件所在目錄,底層封裝了文件的路徑和路徑狀態(tài)。
  3. 字段
  • String path:文件的路徑,可以是根目錄或絕對路徑;
  • PathStatus status = null:路徑的狀態(tài),
  • enum PathStatus { INVALID, CHECKED }:
  • int prefixLength:文件前綴長度;
  • char separatorChar = fs.getSeparator():文件分隔符。
  1. 常用方法
  • File(String pathname):構(gòu)造方法,根據(jù)指定路徑創(chuàng)建一個File文件類對象;
  • File(String parent, String child):構(gòu)造方法,
  • String getName():
  • booLean exists():判斷 根據(jù)指定路徑創(chuàng)建的文件類對象是否存在,即在指定路徑下是否能找到對應文件;
  • boolean createNewFile():若指定路徑下的文件類對象不存在,則在該路徑下以文件形式創(chuàng)建相應的文件;
  • boolean mkdir():若指定路徑下的文件類對象不存在,則在該路徑下以目錄形式創(chuàng)建相應的目錄文件;
  • boolean mkdirs():若指定路徑下的文件類對象不存在,則在該路徑下以多級目錄形式創(chuàng)建相應的目錄文件;
  • String getParent():獲取此路徑下創(chuàng)建的文件類對象的文件父目錄;
  • File getParentFile():獲取此路徑下創(chuàng)建的文件類對象的文件父目錄,并根據(jù)該目錄生成新的文件類對象;
  • String getPath():獲取該文件類對象的原文件路徑;
  • String getAbsolutePath():獲取該文件類對象的文件絕對路徑;
  • boolean isDirectory():判斷該文件類對象是否是一個目錄;
  • boolean isFile():判斷該文件類對象是否是一個文件;
  • boolean isHidden():判斷該文件類對象是否是一個隱藏文件;
  • long lastModified(): 獲取該文件類對象的文件最后一次的修改時間,返回的是自1970年1月1日開始的毫秒數(shù);
  • long length():獲取該文件類對象的文件大??;
  • File[] listFiles():獲取當前目錄下所有的子文件,返回的是由多級子目錄構(gòu)成的文件數(shù)組,可通過foreach循環(huán)遍歷該數(shù)組得到所有的子文件。
  1. 代碼實例
//根據(jù)指定路徑創(chuàng)建一個文件類對象
        File f = new File("");
        // 判斷該路徑下的創(chuàng)建的文件類對象 (文件)是否存在
        System.out.println(f.exists());

        // 如果路徑下文件不存在,則以文件的形式創(chuàng)建出來
        /*if(!f1.exists()) {
            // 以文件形式新建
            f1.createNewFile();
        }*/
        // 如果路徑下文件不存在,則以單級目錄的形式創(chuàng)建出來
        /*if(!f1.exists()) {
            // 以目錄的形式新建。
            f1.mkdir();
        }*/
        // 如果路徑下文件不存在,則以多級目錄的形式創(chuàng)建出來
        File f2 = new File("D:/a/b/c/d/e/f");
        /*if(!f2.exists()) {
            // 以多重目錄的形式新建。
            f2.mkdirs();
        }*/

        File f3 = new File("D:\\course\\01-開課\\學習方法.txt");
        // 獲取文件的父路徑
        String parentPath = f3.getParent();
        // 獲取文件的父路徑文件
        File parentFile = f3.getParentFile();
        //獲取文件的絕對路徑
        System.out.println("獲取絕對路徑:" + parentFile.getAbsolutePath());

常見面試題

  1. 使用字節(jié)文件流實現(xiàn)一個文件的復制?
    答:
    創(chuàng)建字節(jié)輸入流對象FileInputStream;
    創(chuàng)建字節(jié)輸出流對象FileOutPutStream;
    輸入流對象從指定路徑下read()讀取文件,并將讀取的內(nèi)容使用字節(jié)數(shù)組byte[]存儲;
    使用變量返回值判斷文件是否有內(nèi)容可讀;
    輸出流對象將輸入流讀取的字節(jié)數(shù)組中的內(nèi)容write()寫出;
    刷新輸出流;
    關閉所有流,釋放資源;
    重點:一邊讀,一邊寫,每次讀取多少個就寫出多少個。
public class StreamCopyTest {
    public static void main(String[] args) {
        //創(chuàng)建字節(jié)輸入流引用
        FileInputStream fis = null;
        //創(chuàng)建字節(jié)輸出流引用
        FileOutputStream fos = null;
        try {
            //創(chuàng)建實際的字節(jié)輸入流對象
            fis = new FileInputStream("");
            //創(chuàng)建實際的字節(jié)輸出流對象--輸出文件若存在則覆蓋,不存在則新建
            fos = new FileOutputStream("");
            //創(chuàng)建字節(jié)數(shù)組存儲輸入流讀取的數(shù)據(jù)
            byte[] bs = new byte[1024 * 1024];//1024kb = 1byte,1mb = 1024kb
            //用于計算每次讀取文件返回的字節(jié)個數(shù),-1表示已讀到文件末尾
            int readcount = 0;
            //一邊讀取,一邊輸出,讀取多少個,輸出多少個
            while ((readcount = fis.read(bs)) != -1){
                fos.write(bs,0,readcount);
            }
            //輸出完成時,必須刷新管道
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //關閉流資源,釋放內(nèi)存
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 使用字符文件流實現(xiàn)一個文件的復制?
    答:
    創(chuàng)建字符輸入流對象FileReader
    創(chuàng)建字符輸出流對象FileWriter
    創(chuàng)建字符數(shù)組char[]存儲字符輸入流read讀取的文件數(shù)據(jù)
    定義變量返回值 判斷文件是否有數(shù)據(jù)可讀
    字符輸入流從文件讀取數(shù)據(jù)
    字符輸出流將讀取到的數(shù)據(jù)寫出到文件
    刷新輸出流
    關閉所有資源
public class WriterCopyTest {
    public static void main(String[] args) {
        //創(chuàng)建字符輸入流引用
        FileReader fr = null;
        //創(chuàng)建字符輸出流引用
        FileWriter fw = null;
        try {
            //創(chuàng)建實際的字符輸入流對象
            fr = new FileReader("");
            //創(chuàng)建實際的字符輸出流對象,若輸出文件已存在則覆蓋,若不存在則新建
            fw = new FileWriter("");
            //創(chuàng)建字符數(shù)組存儲輸入流每一次讀取的數(shù)據(jù)
            char[] cs = new char[1024*1024];
            //用于判斷每一次讀取文件返回的字節(jié)個數(shù),-1表示已讀到文件末尾
            int readchar = 0;
            //一邊讀取,一邊寫出,讀取多少個寫出多少個
            while ((readchar = fr.read(cs)) != -1){
                fw.write(cs,0,readchar);
            }
            //輸出完成時,必須刷新管道
            fw.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //關閉流資源,釋放內(nèi)存
            if (fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 使用一個字節(jié)緩沖流 或 字符緩沖流實現(xiàn)一個文件的復制?
    答:
    BufferInputStream和BufferOutPutStream
    BufferReader和BufferWriter

  1. 實現(xiàn)從D盤的多個目錄及文件復制到C盤下?
    答:
    使用File類獲取得到多個目錄(數(shù)組)
    遍歷File數(shù)組中得到的所有File對象
    判斷File數(shù)組中的File對象是目錄還是文件
    (是目錄繼續(xù)往下遍歷,是文件則直接復制到另外一個盤下)
    確定源目錄的路徑 和 目標目錄的路徑
    對File對象采用IO流進行邊讀邊寫
    如何確定對應的目錄路徑?
public static void main(String[] args) {
        // 拷貝源
        File srcFile = new File("D:\\course\\02-JavaSE\\document");
        // 拷貝目標
        File destFile = new File("C:\\a\\b\\c");
        // 調(diào)用方法完成拷貝
        copyDir(srcFile, destFile);
    }

    /**
     * 拷貝目錄
     * @param srcFile 拷貝源
     * @param destFile 拷貝目標
     */
    private static void copyDir(File srcFile, File destFile) {
        if(srcFile.isFile()) {
            // srcFile如果是一個文件(文件下面不再有目錄),遞歸結(jié)束
            // 并且是文件即需要拷貝,復制--一邊讀一邊寫
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                // 從源路徑讀取文件
                in = new FileInputStream(srcFile);
                // 處理得出要復制到的目標路徑--路徑的轉(zhuǎn)換很難很重要,需要多重字符串截取和拼接
                //先獲取目標路徑的盤符根目錄--再對源路徑盤符下的子目錄路徑進行復制拼接                
                String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\")  + srcFile.getAbsolutePath().substring(3);
                //將讀取的文件寫入到新的盤符目錄下                
                out = new FileOutputStream(path);
                // 一邊讀一邊寫,
                byte[] bytes = new byte[1024 * 1024]; // 一次復制1MB
                int readCount = 0;
                while((readCount = in.read(bytes)) != -1){
                    out.write(bytes, 0, readCount);
                }
                //寫入完成時刷新管道
                out.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{   //關閉流資源,釋放內(nèi)存        
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return;
        }
        // 獲取源路徑下的所有子目錄(文件)
        File[] files = srcFile.listFiles();
        for(File file : files){
            // 獲取所有文件(包括目錄和文件)的絕對路徑
            //若是源路徑下的File是一個目錄,則在目標盤符下創(chuàng)建對應目錄;若是一個File文件,則結(jié)束遞歸開始復制
            if(file.isDirectory()){
                // 在目標盤符下新建對應的目錄
                //D:\course\02-JavaSE\document\JavaSE進階講義     源目錄
                //C:\course\02-JavaSE\document\JavaSE進階講義     目標目錄
                //源file目錄
                String srcDir = file.getAbsolutePath();
                //新file要復制到的新目錄
                String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\")  + srcDir.substring(3);
                //使用新目錄創(chuàng)建出File文件對象
                File newFile = new File(destDir);
                if(!newFile.exists()){
                    newFile.mkdirs();
                }
            }
            // 遞歸調(diào)用--很難想到使用遞歸
            copyDir(file, destFile);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容