java基礎(四):談談java中的IO流

從入門到放棄的攻城獅發表於2019-02-26

1.位元組流

1.1.位元組輸出流output

1.1.1.資料寫入檔案中

  通過api查詢output。找到很多,其中java.io.OutputStream,OutputStream: 輸出位元組流的超類。

  基本特點:

  1、操作的資料都是位元組。

  2、定義了輸出位元組流的基本共性功能。

  3、輸出流中定義都是寫write方法。操作位元組陣列write(byte[]),操作單個位元組write(byte)。

  子類有規律:所有的子類名稱字尾是父類名,字首名是這個流物件功能

  想要操作檔案: FileOutputStream

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //需求:將資料寫入到檔案中。
        //建立儲存資料的檔案。
        File file = new File("c:\\file.txt");
        //建立一個用於操作檔案的位元組輸出流物件。一建立就必須明確資料儲存目的地。
        //輸出流目的是檔案,會自動建立。如果檔案存在,則覆蓋。
        FileOutputStream fos = new FileOutputStream(file);
        //呼叫父類中的write方法。
        byte[] data = "abcde".getBytes();
        fos.write(data);
        //關閉流資源。
        fos.close();
    }
}
複製程式碼

1.1.2.給檔案中續寫和換行

  我們知道直接new FileOutputStream(file)這樣建立物件,會覆蓋原有的檔案,那麼我們想在原有的檔案中續寫內容怎麼辦呢?繼續查閱FileOutputStream的API。發現在FileOutputStream的建構函式中,可以接受一個boolean型別的值,如果值true,就會在檔案末位繼續新增

public class FileOutputStreamDemo2 {
    public static void main(String[] args) throws Exception {
        File file = new File("c:\\file.txt");
        FileOutputStream fos = new FileOutputStream(file, true);
        String str = "\r\n"+"itcast";
        fos.write(str.getBytes());
        fos.close();
    }
}
複製程式碼

1.1.3.IO異常的處理

  我們在開發中應該如何處理這些異常呢?

public class FileOutputStreamDemo3 {
    public static void main(String[] args) {

        File file = new File("c:\\file.txt");
        //定義FileOutputStream的引用
        FileOutputStream fos = null;
        try {
            //建立FileOutputStream物件
            fos = new FileOutputStream(file);
            //寫出資料
            fos.write("abcde".getBytes());

        } catch (IOException e) {
            System.out.println(e.toString() + "----");
        } finally {
            //一定要判斷fos是否為null,只有不為null時,才可以關閉資源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException("");
                }
            }
        }
    }
}
複製程式碼

1.2.位元組輸入流Input

1.2.1.讀取資料read方法

  通過api查詢input。java.io.InputStream。InputStream:位元組輸入流的超類。

  常見功能:

int read():讀取一個位元組並返回,沒有位元組返回-1.

int read(byte[]): 讀取一定量的位元組數,並儲存到位元組陣列中,返回讀取到的位元組數。

  用於讀取檔案的位元組輸入流物件:FileInputStream。

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        File file = new File("c:\\file.txt");
        //建立一個位元組輸入流物件,必須明確資料來源,其實就是建立位元組讀取流和資料來源相關聯。
        FileInputStream fis = new FileInputStream(file);
        //讀取資料。使用 read();一次讀一個位元組。
        int ch = 0;
        while((ch=fis.read())!=-1){
            System.out.println("ch="+(char)ch);
        }
        // 關閉資源。
        fis.close();
    }
}
複製程式碼

1.2.2.讀取資料read(byte[])方法

  在讀取檔案中的資料時,呼叫read方法,每次只能讀取一個,太麻煩了,於是我們可以定義陣列作為臨時的儲存容器,這時可以呼叫過載的read方法,一次可以讀取多個字元。

public class FileInputStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
         * 演示第二個讀取方法, read(byte[]);
         */
        File file = new File("c:\\file.txt");
        // 建立一個位元組輸入流物件,必須明確資料來源,其實就是建立位元組讀取流和資料來源相關聯。
        FileInputStream fis = new FileInputStream(file);        
        //建立一個位元組陣列。
        byte[] buf = new byte[1024];//長度可以定義成1024的整數倍。        
        int len = 0;
        while((len=fis.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }
        fis.close();
    }
}
複製程式碼

2.字元流

  經過前面的學習,我們基本掌握的檔案的讀寫操作,在操作過程中位元組流可以操作所有資料,可是當我們操作的檔案中有中文字元,並且需要對中文字元做出處理時怎麼辦呢?

2.1.位元組流讀取問題

  通過以下程式讀取帶有中檔案的檔案。

public class CharStreamDemo {
    public static void main(String[] args) throws IOException {
        //給檔案中寫中文
        writeCNText();
        //讀取檔案中的中文
        readCNText();
    }    
    //讀取中文
    public static void readCNText() throws IOException {
        FileInputStream fis = new FileInputStream("c:\\cn.txt");
        int ch = 0;
        while((ch = fis.read())!=-1){
            System.out.println(ch);
        }
    }
    //寫中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("c:\\cn.txt");
        fos.write("a傳智播客歡迎你".getBytes());
        fos.close();
    }
}
複製程式碼

  上面程式在讀取含有中文的檔案時,我們並沒有看到具體的中文,而是看到一些數字,這是什麼原因呢?既然看不到中文,那麼我們如何對其中的中文做處理呢?要解決這個問題,我們必須研究下字元的編碼過程。

2.2.編碼表

  我們知道計算機底層資料儲存的都是二進位制資料,而我們生活中的各種各樣的資料,如何才能和計算機中儲存的二進位制資料對應起來呢?這時老美他們就把每一個字元和一個整數對應起來,就形成了一張編碼表,老美他們的編碼表就是ASCII表。其中就是各種英文字元對應的編碼。

編碼表:其實就是生活中字元和計算機二進位制的對應關係表。

  • ascii: 一個位元組中的7位就可以表示。對應的位元組都是正數。0-xxxxxxx

  • iso8859-1:拉丁碼錶 latin,用了一個位元組用的8位。1-xxxxxxx 負數。

  • GB2312:簡體中文碼錶。包含6000-7000中文和符號。用兩個位元組表示。兩個位元組都是開頭為1 ,兩個位元組都是負數。

  • GBK:目前最常用的中文碼錶,2萬的中文和符號。用兩個位元組表示,其中的一部分文字,第一個位元組開頭是1,第二位元組開頭是0

  • GB18030:最新的中文碼錶,目前還沒有正式使用。

  • unicode:國際標準碼錶:無論是什麼文字,都用兩個位元組儲存。

   Java中的char型別用的就是這個碼錶。char c = 'a';佔兩個位元組。

   Java中的字串是按照系統預設碼錶來解析的。簡體中文版 字串預設的碼錶是GBK。

  • UTF-8:基於unicode,一個位元組就可以儲存資料,不要用兩個位元組儲存,而且這個碼錶更加的標準化,在每一個位元組頭加入了編碼資訊(後期到api中查詢)。

  能識別中文的碼錶:GBK、UTF-8;正因為識別中文碼錶不唯一,涉及到了編碼解碼問題。對於我們開發而言;常見的編碼 GBK UTF-8 ISO8859-1

    文字--->(數字) :編碼。

    (數字)--->文字 : 解碼。

2.3.FileReader類介紹

  上述程式中我們讀取擁有中文的檔案時,使用的位元組流在讀取,那麼我們讀取到的都是一個一個位元組。只要把這些位元組去查閱對應的編碼表,就能夠得到與之對應的字元。API中是否給我們已經提供了讀取相應字元的功能流物件呢?

  查閱FileInputStream的API,發現FileInputStream 用於讀取諸如影像資料之類的原始位元組流。要讀取字元流,請考慮使用 FileReader。如果讀取字元流,要使用FileReader,FileReader是什麼呢?

  開啟FileReader的API介紹。用來讀取字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是適當的,原來FileReader 用於讀取字元流。使用FileReader時,瞭解它的功能,看它所屬的體系頂層。

  Reader:讀取字元流的抽象超類。

    read():讀取單個字元並返回

    read(char[]):將資料讀取到陣列中,並返回讀取的個數。

public class CharStreamDemo {
    public static void main(String[] args) throws IOException {
        //給檔案中寫中文
        writeCNText();
        //讀取檔案中的中文
        readCNText();
    }    
    //讀取中文
    public static void readCNText() throws IOException {
        FileReader fr = new FileReader("D:\\test\\cn.txt");
        int ch = 0;
        while((ch = fr.read())!=-1){
            //輸出的字元對應的編碼值
            System.out.println(ch);
            //輸出字元本身
            System.out.println((char)ch);
        }
    }
    //寫中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\test\\cn.txt");
        fos.write("a傳智播客歡迎你".getBytes());
        fos.close();
    }
}
複製程式碼

2.4.FileWriter類介紹

  既然有專門用於讀取字元的流物件,那麼肯定也有寫的字元流物件,查閱API,發現有一個Writer類,Writer是寫入字元流的抽象類。其中描述了相應的寫的動作。

public class FileWriterDemo {
    public static void main(String[] args) throws IOException {
        //演示FileWriter 用於操作檔案的便捷類。
        FileWriter fw = new FileWriter("d:\\text\\fw.txt");
        
        fw.write("你好謝謝再見");//這些文字都要先編碼。都寫入到了流的緩衝區中。
        
        fw.flush();
        
        fw.close();
    }
}
複製程式碼

2.5.flush()和close()的區別

  flush():將流中的緩衝區緩衝的資料重新整理到目的地中,重新整理後,流還可以繼續使用。

  close():關閉資源,但在關閉前會將緩衝區中的資料先重新整理到目的地,否則丟失資料,然後在關閉流。流不可以使用。如果寫入資料多,一定要一邊寫一邊重新整理,最後一次可以不重新整理,由close完成重新整理並關閉。

3.轉換流

  學習完了使用字元流對檔案的簡單操作後,在學習字元流的時候,其中說如果需要指定編碼和緩衝區大小時,可以在位元組流的基礎上,構造一個InputStreamReader或者OutputStreamWriter,這又是什麼意思呢?

3.1.OutputSreamWriter類

  需求:既然識別中文的碼錶有兩個,GBK、UTF-8等,能不能將中文資料按照utf-8的方式進行檔案的儲存呢?

  還能使用FileWriter嗎?不能使用了,因為FileWriter中預設的是GBK。通過FileWriter的api描述,要指定編碼表這些值,需要使用OutputStreamWriter

  OutputStreamWriter 是字元流通向位元組流的橋樑:可使用指定的 charset 將要寫入流中的字元編碼成位元組。它的作用的就是,將字串按照指定的編碼表轉成位元組,在使用位元組流將這些位元組寫出去

public static void writeCN() throws Exception {
        //建立與檔案關聯的位元組輸出流物件
        FileOutputStream fos = new FileOutputStream("c:\\cn8.txt");
        //建立可以把字元轉成位元組的轉換流物件,並指定編碼
        OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
        //呼叫轉換流,把文字寫出去,其實是寫到轉換流的緩衝區中
        osw.write("你好");//寫入緩衝區。
        osw.close();
    }
複製程式碼

  OutputStreamWriter流物件,它到底如何把字元轉成位元組輸出的呢?

  其實在OutputStreamWriter流中維護自己的緩衝區,當我們呼叫OutputStreamWriter物件的write方法時,會拿著字元到指定的碼錶中進行查詢,把查到的字元編碼值轉成位元組數存放到OutputStreamWriter緩衝區中。然後再呼叫重新整理功能,或者關閉流,或者緩衝區存滿後會把緩衝區中的位元組資料使用位元組流寫到指定的檔案中。

3.2.InputSreamReader類

  查閱InputStreamReader的API介紹,InputStreamReader 是位元組流通向字元流的橋樑:它使用指定的 charset 讀取位元組並將其解碼為字元。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺預設的字符集。

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        //演示位元組轉字元流的轉換流
        readCN();
    }
    public static void readCN() throws IOException{
        //建立讀取檔案的位元組流物件
        InputStream in = new FileInputStream("c: \\cn8.txt");
        //建立轉換流物件 
        //InputStreamReader isr = new InputStreamReader(in);這樣建立物件,會用本地預設碼錶讀取,將會發生錯誤解碼的錯誤
        InputStreamReader isr = new InputStreamReader(in,"utf-8");
        //使用轉換流去讀位元組流中的位元組
        int ch = 0;
        while((ch = isr.read())!=-1){
            System.out.println((char)ch);
        }
        //關閉流
        isr.close();
    }
}
複製程式碼

  注意:在讀取指定的編碼的檔案時,一定要指定編碼格式,否則就會發生解碼錯誤,而發生亂碼現象

3.3.轉換流和子類的區別

  發現有如下繼承關係:

    OutputStreamWriter:

      FileWriter:

    InputStreamReader:

      FileReader;

  父類和子類的功能有什麼區別呢?

  OutputStreamWriter和InputStreamReader是字元和位元組的橋樑:也可以稱之為字元轉換流。字元轉換流原理:位元組流+編碼表。

  FileWriter和FileReader:作為子類,僅作為操作字元檔案的便捷類存在。當操作的字元檔案,使用的是預設編碼表時可以不用父類,而直接用子類就完成操作了,簡化了程式碼。

  InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//預設字符集。

  InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。

  FileReader fr = new FileReader("a.txt");

  這三句程式碼的功能是一樣的,其中第三句最為便捷。

  注意:一旦要指定其他編碼時,絕對不能用子類,必須使用字元轉換流。什麼時候用子類呢?

  條件:

  1、操作的是檔案。2、使用預設編碼。3 純文字

  總結:

  位元組--->字元 : 看不懂的--->看的懂的。 需要讀。輸入流。 InputStreamReader

  字元--->位元組 : 看的懂的--->看不懂的。 需要寫。輸出流。 OutputStreamWriter

4.緩衝流

  在我們學習位元組流與字元流的時候,大家都進行過讀取檔案中資料的操作,讀取資料量大的檔案時,讀取的速度會很慢,很影響我們程式的效率,那麼, 我想提高速度,怎麼辦?Java中提高了一套緩衝流,它的存在,可提高IO流的讀寫速度緩衝流,根據流的分類分類位元組緩衝流與字元緩衝流。

4.1.位元組緩衝流

  位元組緩衝流根據流的方向,共有2個

     寫入資料到流中,位元組緩衝輸出流 BufferedOutputStream

    讀取流中的資料,位元組緩衝輸入流 BufferedInputStream

  它們的內部都包含了一個緩衝區,通過緩衝區讀寫,就可以提高了IO流的讀寫速度

4.1.1.位元組緩衝輸出流BufferedOutputStream

  通過位元組緩衝流,進行檔案的讀寫操作 寫資料到檔案的操作

  構造方法public BufferedOutputStream(OutputStream out)建立一個新的緩衝輸出流,以將資料寫入指定的底層輸出流。

public class BufferedOutputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        
        //寫資料到檔案的方法
        write();
    }

    /*
     * 寫資料到檔案的方法
     * 1,建立流
     * 2,寫資料
     * 3,關閉流
     */
    private static void write() throws IOException {
        //建立基本的位元組輸出流
        FileOutputStream fileOut = new FileOutputStream("abc.txt");
        //使用高效的流,把基本的流進行封裝,實現速度的提升
        BufferedOutputStream out = new BufferedOutputStream(fileOut);
        //2,寫資料
        out.write("hello".getBytes());
        //3,關閉流
        out.close();
    }
}
複製程式碼

4.1.2.位元組緩衝輸出流BufferedInputStream

  剛剛我們學習了輸出流實現了向檔案中寫資料的操作,那麼,現在我們完成讀取檔案中資料的操作

  構造方法

  public BufferedInputStream(InputStream in)

/*
     * 從檔案中讀取資料
     * 1,建立緩衝流物件
     * 2,讀資料,列印
     * 3,關閉
     */
    private static void read() throws IOException {
        //1,建立緩衝流物件
        FileInputStream fileIn = new FileInputStream("abc.txt");
        //把基本的流包裝成高效的流
        BufferedInputStream in = new BufferedInputStream(fileIn);
        //2,讀資料
        int ch = -1;
        while ( (ch = in.read()) != -1 ) {
            //列印
            System.out.print((char)ch);
        }
        //3,關閉
        in.close();
    }
複製程式碼

4.1.3.使用基本的流與高效的流完成複製檔案

  我們一直在說,高效的流速度快並高效,怎麼體現呢?需要通過一個複製檔案耗時的比較過程,來體驗一下高效流帶來的快感。

/*
 * 需求:將d:\\test.avi檔案進行復制
 *         採用4種方式複製
 *         方式1: 採用基本的流,一次一個位元組的方式複製    共耗時 224613毫秒
 *         方式2: 採用基本的流,一個多個位元組的方式賦值    共耗時     327毫秒
 *         方式3: 採用高效的流,一次一個位元組的方式複製    共耗時    2047毫秒
 *         方式4: 採用高效的流,一個多個位元組的方式賦值    共耗時      96毫秒
 * 
 * 資料來源: d:\\test.avi
 * 目的地1: d:\\copy1.avi
 * 目的地2: d:\\copy2.avi
 * 目的地3: d:\\copy3.avi
 * 目的地4: d:\\copy4.avi
 * 
 * 實現的步驟:
 *     1,指定資料來源
 *     2,指定目的地
 *     3,讀資料
 *     4,寫資料
 *     5,關閉流
 * 
 */
public class CopyAVI {
    public static void main(String[] args) throws IOException {
        //開始計時
        long start = System.currentTimeMillis();
        //方式1: 採用基本的流,一次一個位元組的方式複製
        //method1("d:\\test.avi", "d:\\copy1.avi");
        //方式2: 採用基本的流,一個多個位元組的方式賦值
        //method2("d:\\test.avi", "d:\\copy2.avi");
        //方式3: 採用高效的流,一次一個位元組的方式複製
        //method3("d:\\test.avi", "d:\\copy3.avi");
        //方式4: 採用高效的流,一個多個位元組的方式賦值
        method4("d:\\test.avi", "d:\\copy4.avi");
        
        //結束計時
        long end = System.currentTimeMillis();
        //列印耗時多少毫秒
        System.out.println("共耗時 " +(end - start)+ "毫秒");
    }
    
    //方式4: 採用高效的流,一個多個位元組的方式賦值
    private static void method4(String src, String dest) throws IOException {
        //1,指定資料來源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
         //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
         //3,讀資料
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len = in.read(buffer)) != -1) {
            //4,寫資料
            out.write(buffer, 0, len);
        }
         //5,關閉流
        in.close();
        out.close();
    }

    //方式3: 採用高效的流,一次一個位元組的方式複製
    private static void method3(String src, String dest) throws IOException {
        //1,指定資料來源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
         //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
         //3,讀資料
        int ch = -1;
        while ((ch=in.read()) != -1) {
            //4,寫資料
            out.write(ch);    
        }        
         //5,關閉流
        in.close();
        out.close();
    }

    //方式2: 採用基本的流,一個多個位元組的方式賦值
    private static void method2(String src, String dest) throws IOException {
        //1,指定資料來源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,讀資料
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len=in.read(buffer)) != -1) {
            //4,寫資料
            out.write(buffer, 0, len);
        }
        //5,關閉流
        in.close();
        out.close();
    }

    //方式1: 採用基本的流,一次一個位元組的方式複製
    private static void method1(String src, String dest) throws IOException {
        //1,指定資料來源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,讀資料
        int ch = -1;
        while (( ch=in.read()) != -1) {
            //4,寫資料
            out.write(ch);
        }
        //5,關閉流
        in.close();
        out.close();
    }
}
複製程式碼

4.2.字元緩衝流

  字元緩衝輸入流 BufferedReader

  字元緩衝輸出流 BufferedWriter

  完成文字資料的高效的寫入與讀取的操作

4.2.1.字元緩衝輸出流BufferedWriter

  將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。

  方法:void newLine() 根據當前的系統,寫入一個換行符

/*
 * BufferedWriter 字元緩衝輸出流
 * 方法
 *     public void newLine()寫入一個行分隔符
 * 
 * 需求: 通過緩衝輸出流寫入資料到檔案
 * 分析:
 *     1,建立流物件
 *     2,寫資料
 *     3,關閉流
 * 
 */
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        //建立流
        //基本字元輸出流
        FileWriter fileOut = new FileWriter("file.txt");
        //把基本的流進行包裝
        BufferedWriter out = new BufferedWriter(fileOut);
        //2,寫資料
        for (int i=0; i<5; i++) {
            out.write("hello");
            out.newLine();
        }
        //3,關閉流
        out.close();
    }
}
複製程式碼

4.2.2.字元緩衝輸入流BufferedReader

  從字元輸入流中讀取文字,緩衝各個字元,從而實現字元、陣列和行的高效讀取。

  方法public String readLine() 讀取一個文字行,包含該行內容的字串,不包含任何行終止符,如果已到達流末尾,則返回 null

/*
 * BufferedReader 字元緩衝輸入流
 * 
 * 方法:
 *     String readLine() 
 * 需求:從檔案中讀取資料,並顯示資料
 */
public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //1,建立流
        BufferedReader in = new BufferedReader(new FileReader("file.txt"));
        //2,讀資料
        //一次一個字元
        //一次一個字元陣列
        //一次讀取文字中一行的字串內容
        String line = null;
        while( (line = in.readLine()) != null ){
            System.out.println(line);
        }
        
        //3,關閉流
        in.close();
    }
}
複製程式碼

4.2.3.使用字元緩衝流完成文字檔案的複製

  剛剛我們學習完了緩衝流,現在我們就使用字元緩衝流的特有功能,完成文字檔案的複製

/*
 * 採用高效的字元緩衝流,完成文字檔案的賦值
 * 
 * 資料來源: file.txt
 * 目的地: copyFile.txt
 * 
 * 分析:
 *     1,指定資料來源, 是資料來源中讀資料,採用輸入流
 *     2,指定目的地,是把資料寫入目的地,採用輸出流
 *     3,讀資料
 *     4,寫資料
 *     5,關閉流
 */
public class CopyTextFile {
    public static void main(String[] args) throws IOException {
        //1,指定資料來源, 是資料來源中讀資料,採用輸入流
        BufferedReader in = new BufferedReader(new FileReader("file.txt"));
        //2,指定目的地,是把資料寫入目的地,採用輸出流
        BufferedWriter out = new BufferedWriter(new FileWriter("copyFile.txt"));
        //3,讀資料
        String line = null;
        while ( (line = in.readLine()) != null ) {
            //4,寫資料
            out.write(line);
            //寫入換行符號
            out.newLine();
        }
        //5,關閉流
        out.close();
        in.close();
    }
}
複製程式碼

5.Properties類

5.1.Properties類介紹

  Properties特點:

    1、Hashtable的子類,map集合中的方法都可以用。

    2、該集合沒有泛型。鍵值都是字串。

    3、它是一個可以持久化的屬性集。鍵值可以儲存到集合中,也可以儲存到持久化的裝置(硬碟、U盤、光碟)上。鍵值的來源也可以是持久化的裝置。

    4、有和流技術相結合的方法。

      a.load(InputStream) 把指定流所對應的檔案中的資料,讀取出來,儲存到Propertie集合中

      b. load(Reader)

      c. store(OutputStream,commonts)把集合中的資料,儲存到指定的流所對應的檔案中,引數commonts代表對描述資訊

      d.stroe(Writer,comments);

/*
 * 
 * Properties集合,它是唯一一個能與IO流互動的集合
 * 
 * 需求:向Properties集合中新增元素,並遍歷
 * 
 * 方法:
 * public Object setProperty(String key, String value)呼叫 Hashtable 的方法 put。
 * public Set<String> stringPropertyNames()返回此屬性列表中的鍵集,
 * public String getProperty(String key)用指定的鍵在此屬性列表中搜尋屬性
 */
public class PropertiesDemo01 {
    public static void main(String[] args) {
        //建立集合物件
        Properties prop = new Properties();
        //新增元素到集合
        //prop.put(key, value);
        prop.setProperty("周迅", "張學友");
        prop.setProperty("李小璐", "賈乃亮");
        prop.setProperty("楊冪", "劉愷威");
        
        //System.out.println(prop);//測試的使用
        //遍歷集合
        Set<String> keys = prop.stringPropertyNames();
        for (String key : keys) {
            //通過鍵 找值
            //prop.get(key)
            String value = prop.getProperty(key);
            System.out.println(key+"==" +value);
        }
    }
}
複製程式碼

將配置檔案中的資料儲存到檔案中

/**
 * 需求:使用Properties集合,完成把集合內容儲存到IO流所對應檔案中的操作
 * 
 * 分析:
 *     1,建立Properties集合
 *     2,新增元素到集合
 *     3,建立流
 *     4,把集合中的資料儲存到流所對應的檔案中
 *        store(OutputStream,commonts)把集合中的資料,儲存到指定的流所對應的檔案中,引數commonts代表對描述資訊
 *        stroe(Writer,comments);
 *     5,關閉流
 */
public class PropertiesDemo02 {
    public static void main(String[] args) throws IOException {
        //1,建立Properties集合
        Properties prop = new Properties();
        //2,新增元素到集合
        prop.setProperty("周迅", "張學友");
        prop.setProperty("李小璐", "賈乃亮");
        prop.setProperty("楊冪", "劉愷威");
        
        //3,建立流
        FileWriter out = new FileWriter("prop.properties");
        //4,把集合中的資料儲存到流所對應的檔案中
        prop.store(out, "save data");
        //5,關閉流
        out.close();
    }
}
複製程式碼

讀取配置檔案中的資料,同時更新資料,並儲存

/*
 * 需求:從屬性集檔案prop.properties 中取出資料,儲存到集合中
 * 分析:
 *     1,建立集合
 *     2,建立流物件
 *     3,把流所對應檔案中的資料 讀取到集合中
 *        load(InputStream)  把指定流所對應的檔案中的資料,讀取出來,儲存到Propertie集合中
        load(Reader)  
 *     4,關閉流
 *     5,顯示集合中的資料
 */
public class PropertiesDemo03 {
    public static void main(String[] args) throws IOException {
        //1,建立集合
        Properties prop = new Properties();
        //2,建立流物件
        FileInputStream in = new FileInputStream("prop.properties");
//FileReader in = new FileReader("prop.properties");
        //3,把流所對應檔案中的資料 讀取到集合中
        prop.load(in);
        //4,關閉流
        in.close();
        //5,顯示集合中的資料
        System.out.println(prop);
        
    }
複製程式碼

注意:使用字元流FileReader就可以完成檔案中文的讀取操作了

6.序列化流與煩序列化流

6.1.物件序列化流

  用於從流中讀取物件的操作流 ObjectInputStream 稱為 反序列化流

  用於向流中寫入物件的操作流 ObjectOutputStream 稱為 序列化流

  特點:用於操作物件。

  解決問題:可以將物件進行序列化和反序列化

  注意:物件序列化一定要實現Serializable介面。為了給類定義一個serialVersionUID。

  功能:

   ObjectInputStream readObject()

   ObjectOutputStream writeObject()

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
         * 將一個物件儲存到持久化(硬碟)的裝置上。
         */
        writeObj();//物件的序列化。
    }
    public static void writeObj() throws IOException {
        //1,明確儲存物件的檔案。
        FileOutputStream fos = new FileOutputStream("tempfile\\obj.object");
        //2,給操作檔案物件加入寫入物件功能。
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //3,呼叫了寫入物件的方法。
        oos.writeObject(new Person("wangcai",20));
        //關閉資源。
        oos.close();
    }
}
複製程式碼

6.2.物件反序列化流

  當把一個物件持久化儲存起來之後,需要使用反序列化技術獲取儲存起來的物件。使用此ObjectInputStream物件就可以完成反序列化動作

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        readObj();//物件的反序列化。
    }
    public static void readObj() throws IOException, ClassNotFoundException {
        
        //1,定義流物件關聯儲存了物件檔案。
        FileInputStream fis = new FileInputStream("tempfile\\obj.object");
        
        //2,建立用於讀取物件的功能物件。
        ObjectInputStream ois = new ObjectInputStream(fis);
        
        Person obj = (Person)ois.readObject();
        
        System.out.println(obj.toString());
        
    }
}
複製程式碼

6.3.序列化介面

  當一個物件要能被序列化,這個物件所屬的類必須實現Serializable介面。否則會發生異常NotSerializableException異常。

  同時當反序列化物件時,如果物件所屬的class檔案在序列化之後進行的修改,那麼進行反序列化也會發生異常InvalidClassException。發生這個異常的原因如下:

     該類的序列版本號與從流中讀取的類描述符的版本號不匹配

     該類包含未知資料型別

     該類沒有可訪問的無引數構造方法

  Serializable標記介面。該介面給需要序列化的類,提供了一個序列版本號。serialVersionUID. 該版本號的目的在於驗證序列化的物件和對應類是否版本匹配。

public class Person implements Serializable {

    //給類顯示宣告一個序列版本號。
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    public Person() {
        super();
        
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
複製程式碼

7.總結

  • 位元組流

   位元組輸入流 InputStream

    FileInputStream 操作檔案的位元組輸入流

    BufferedInputStream高效的位元組輸入流

     ObjectInputStream 反序列化流(操作物件的位元組輸入流)

   位元組輸出流 OutputStram

     FileOutputStream 操作檔案的位元組輸出流

    BufferedOutputStream 高效的位元組輸出流

     ObjectOuputStream 序列化流(操作物件的位元組輸出流)

  • 字元流

   字元輸入流 Reader

    FileReader 操作檔案的字元輸入流

     BufferedReader 高效的字元輸入流

     InputStreamReader 輸入操作的轉換流(把位元組流封裝成字元流)

   字元輸出流 Writer

    FileWriter 操作檔案的字元輸出流

     BufferedWriter 高效的字元輸出流

     OutputStreamWriter 輸出操作的轉換流(把位元組流封裝成字元流)

              將字元流轉換成位元組流去儲存

  • 方法:

  讀資料方法:

    read() 一次讀一個位元組或字元的方法

    read(byte[] char[]) 一次讀一個陣列資料的方法

    readLine() 一次讀一行字串的方法(BufferedReader類特有方法)

     readObject() 從流中讀取物件(ObjectInputStream特有方法)

  寫資料方法:

    write(int) 一次寫一個位元組或字元到檔案中

    write(byte[] char[]) 一次寫一個陣列資料到檔案中

     write(String) 一次寫一個字串內容到檔案中

     writeObject(Object ) 寫物件到流中(ObjectOutputStream類特有方法)

    newLine() 寫一個換行符號(BufferedWriter類特有方法)

  • 向檔案中寫入資料的過程

  1,建立輸出流物件

  2,寫資料到檔案

  3,關閉輸出流

  • 從檔案中讀資料的過程

  1, 建立輸入流物件

  2, 從檔案中讀資料

  3, 關閉輸入流

  • 檔案複製的過程

  1, 建立輸入流(資料來源)

  2, 建立輸出流(目的地)

  3, 從輸入流中讀資料

  4, 通過輸出流,把資料寫入目的地

  5, 關閉流

  • Properties:Map集合的一種,它是Hashtable集合的子集合,它鍵與值都是String型別,它是唯一能與IO流結合使用的集合

  方法

  load( InputStream in ) 從流所對應的檔案中,讀資料到集合中

   load( Reader in ) 從流所對應的檔案中,讀資料到集合中

  store( OutputStream out , String message ) 把集合中的資料,寫入到流所對應的檔案中

  store( Writer out , String message) 把集合中的資料,寫入到流所對應的檔案中

實現檔案內容的自動追加

構造方法

   FileOutputStream(File file, boolean append)

   FileOutputStream(String fileName, boolean append)

   FileWriter(File, boolean append)

   FileWriter(String fileName, boolean append)

參考:

java程式設計思想

黑馬教學視訊

www.cnblogs.com/dz-boss/p/1…

相關文章