天天看點

Java的IO系統--位元組流的一些子類(進階流)

這篇來介紹一些位元組流的進階流,在基礎的檔案流上加入了相應的功能,比如緩沖區,寫基本資料類型和對象.

BufferedOutputStream與BufferedInputStream(緩沖位元組流)

簡介

緩沖位元組流在流中維護了一個緩沖區,與基本的檔案流相比,資料首先存儲在緩沖區中,緩沖區充滿或調用相應的方法時,才會把資料沖刷出去,與一個一個位元組的處理相比,效率提升了很多.對于緩沖流,我們可以定義緩沖區大小.

構造器

BufferedInputStream
    BufferedInputStream(InputStream in) 
    封裝一個位元組輸入流,調用了BufferedInputStream(InputStream out, int size),size定義為8192.
    BufferedInputStream(InputStream in, int size) 
    封裝一個位元組輸入流,并定義緩沖區的位元組數.  

BufferedOutputStream
    BufferedOutputStream(OutputStream out) 
    封裝一個位元組輸出流,調用了BufferedOutputStream(OutputStream out, int size),size定義為8192.
    BufferedOutputStream(OutputStream out, int size) 
    封裝一個位元組輸出流,并定義緩沖區的位元組數.  

           

方法

基本上都是前面介紹過的了,在輸出流提供了一個flush(),然後在調用close()的時候會自動調用flush().

先定義幾個方法:

/**
     * 讀取資料并傳回讀取到了幾個位元組.
     * @param is 一個位元組輸入流
     * @throws IOException IO異常
     */
    private static void read(InputStream is) throws IOException {
        read(is, false);
    }

    /**
     * 寫入若幹位元組到檔案
     * @param os 一個位元組輸出流
     * @param bytes 要寫入的位元組數
     * @throws IOException IO異常
     */
    private static void write(OutputStream os, int bytes) throws IOException {
        write(os, bytes, false);
    }


    /**
     * 讀取資料并傳回讀取到了幾個位元組
     * @param is 一個位元組輸入流
     * @param close 讀取結束後是否關閉流
     * @throws IOException IO異常
     */
    private static void read(InputStream is, boolean close) throws IOException {
        int number = 0;
        while (is.read() >= 0) {
            number++;
        }
        if (close) {
            is.close();
        }
        System.out.println("讀取到了" + number + "位元組.");
    }

    /**
     * 增加了是否自動沖刷緩沖區的判定
     * @param os 一個位元組輸出流
     * @param bytes 要寫入的位元組數
     * @param flush 是否調用flush
     * @throws IOException IO異常
     */
    private static void write(OutputStream os, int bytes, boolean flush) throws IOException {
        for (int i = 0; i < bytes; i++) {
            os.write(1);
        }
        if (flush) {
            os.flush();
        }
    }
           

可以看到提供了定義緩沖區大小的構造器,下面來看看緩沖區的作用,首先定義一個預設緩沖區大小的輸出流,寫入緩沖區大小-1個位元組,然後讀取:

public static void main(String[] args) throws IOException {
        File file1 = new File("test1.txt");
        if (file1.exists()) {
            System.out.println("檔案已删除?" + file1.delete());
        }
        FileOutputStream fos1 = new FileOutputStream(file1);
        BufferedOutputStream bos1 = new BufferedOutputStream(fos1);
        write(bos1, 8191);
        FileInputStream fis1 = new FileInputStream(file1);
        BufferedInputStream bis1 = new BufferedInputStream(fis1);
        read(bis1, true);
    }

        結果:檔案已删除?true
             讀取到了0位元組.
           

可以看到雖然檔案是存在的,但是資料實際上并沒有寫入檔案,下面調用flush看看:

public static void main(String[] args) throws IOException {
        ......
        write(bos1, 8191,true);//為true則調用flush().
        ......
    }

    結果:檔案已删除?true
         讀取到了8191位元組.
           

可以看到讀取到了寫入的位元組量,下面再看看寫入緩沖區+1位元組數且不調用flush()的情況.

public static void main(String[] args) throws IOException {
    ......    
    write(bos1, 8193);//未調用flush(),寫入量為緩沖區+1位元組數.
    ......
}

    結果:檔案已删除?true
         讀取到了8192位元組.
           

可以看到隻寫出了緩沖區大小的資料,還剩1個位元組在緩沖區中.下面改變緩沖區的大小,這裡改為4096位元組,寫入8191位元組:

public static void main(String[] args) throws IOException {
        ......
        BufferedOutputStream bos1 = new BufferedOutputStream(fos1, 4096);
        write(bos1, 8191);
        BufferedInputStream bis1 = new BufferedInputStream(fis1, 4096);
        ......
    }

    結果:檔案已删除?true
         讀取到了4096位元組.
           

确實隻寫出了緩沖區大小的資料,剩下的4095位元組并未寫入. 

具體有什麼用處,我們實際操作一個大型檔案看看用時就知道了,測試檔案大小約500MB,在記憶體盤測試,可以完全消除硬碟速度引起的瓶頸.緩沖流提供了預設緩沖區和1024位元組緩沖區的對比,這樣可以測試緩沖區大小對效率的影響,測試運作三次,消除影響:

public static void main(String[] args) throws IOException, InterruptedException {
        long bytes = 0;
        File file = new File("Z:" + File.separator + "test1");
        FileInputStream fis = new FileInputStream(file);
        long start = System.currentTimeMillis();
        while (fis.read() >= 0) {
            bytes++;
        }
        long end = System.currentTimeMillis();
        System.out.println("讀取" + bytes + "位元組," + 
        bytes/1024/1024 + "MB,檔案位元組流用時" + (end - start)/1000 + "秒.");

        //第二次運作
        file = new File("Z:" + File.separator + "test1");
        fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        ......
        System.out.println("讀取" + bytes + "位元組," + 
        bytes/1024/1024 + "MB,緩沖檔案位元組流用時" + (end - start)/1000 + "秒,緩沖區大小為預設8192位元組");
        
        //第三次運作
        file = new File("Z:" + File.separator + "test1");
        fis = new FileInputStream(file);
        bis = new BufferedInputStream(fis, 1024);
        ......
        System.out.println("讀取" + bytes + "位元組," +
        bytes/1024/1024 + "MB,緩沖檔案位元組流用時" + (end - start)/1000 + "秒,緩沖區大小為1024位元組");
    }
           

運作結果如下:

讀取513409024位元組,489MB,檔案位元組流用時360秒.
    讀取513409024位元組,489MB,緩沖檔案位元組流用時7秒,緩沖區大小為預設8192位元組.
    讀取513409024位元組,489MB,緩沖檔案位元組流用時11秒,緩沖區大小為1024位元組
           

可以看到有了緩沖區速率大大提升,緩沖區大小也會影響速率. 

DataOutputStream與DataInputStream(資料位元組流)

簡介

可以讀寫基本資料類型的流,封裝了讀寫讀寫基本資料類型的方法.

構造器

DataInputStream
    DataInputStream(InputStream in) 

DataOutputStream
    DataOutputStream(OutputStream out) 
           

 方法

先來看看不是讀寫資料的方法:

DataInputStream
    int skipBytes(int n) 
    跳過若幹位元組讀取資料.

DataOutputStream
    void flush() 
    沖刷這個流.  
    int size() 
    傳回已經寫出的位元組數  
           

都是已經見過的方法了,這裡就不介紹了,下面看看讀寫資料的方法:

輸入輸出共有的:   
    boolean readBoolean() void writeBoolean()  
    byte readByte()       void writeByte()                                          
    char readChar()       void writeChar() 
    double readDouble()   void writeDouble()
    float readFloat()     void writeFloat()
    int readInt()         void writeInt()
    long readLong()       void writeLong()
    short readShort()     void writeShort()
    String readUTF()      void writeUTF()
    int read(byte[] b, int off, int len)  void write(byte[] b, int off, int len) 

DataInputStream獨有的讀取方法
    int read(byte[] b)
    void readFully(byte[] b)   
    void readFully(byte[] b, int off, int len) 
    String readLine()   已棄用 
    int readUnsignedByte() 
    int readUnsignedShort() 
    static String readUTF(DataInput in) 

DataOutputStream獨有的寫入方法
    void write(int b) 将傳入的b的低八位寫入.
           

這裡隻測試讀寫基本資料類型的方法,測試方法是先寫入再讀出:

public static void main(String[] args) throws IOException {
        File data = new File("data.txt");

        FileOutputStream fos = new FileOutputStream(data);
        DataOutputStream dos = new DataOutputStream(fos);
        dos.writeBoolean(true);
        dos.writeByte(1);
        dos.writeChar('c');
        dos.writeDouble(Math.PI);
        dos.writeFloat(3.14F);
        dos.writeInt(Integer.MAX_VALUE);
        dos.writeLong(Long.MAX_VALUE);
        dos.writeShort(987);
        dos.writeUTF("UTF資料");

        FileInputStream fis = new FileInputStream(data);
        DataInputStream dis = new DataInputStream(fis);
        System.out.println("讀取寫入的boolean類型資料:" + dis.readBoolean());
        System.out.println("讀取寫入的byte類型資料:" + dis.readByte());
        System.out.println("讀取寫入的char類型資料:" + dis.readChar());
        System.out.println("讀取寫入的double類型資料:" + dis.readDouble());
        System.out.println("讀取寫入的float類型資料:" + dis.readFloat());
        System.out.println("讀取寫入的int類型資料:" + dis.readInt());
        System.out.println("讀取寫入的long類型資料:" + dis.readLong());
        System.out.println("讀取寫入的short類型資料:" + dis.readShort());
        System.out.println("讀取寫入的String類型資料:" + dis.readUTF());
    }
           

運作結果: 

讀取寫入的boolean類型資料:true
讀取寫入的byte類型資料:1
讀取寫入的char類型資料:c
讀取寫入的double類型資料:3.141592653589793
讀取寫入的float類型資料:3.14
讀取寫入的int類型資料:2147483647
讀取寫入的long類型資料:9223372036854775807
讀取寫入的short類型資料:987
讀取寫入的String類型資料:UTF資料