1. 程式人生 > >J05-Java IO流總結五 《 BufferedInputStream和BufferedOutputStream 》

J05-Java IO流總結五 《 BufferedInputStream和BufferedOutputStream 》

com ioe 自定義 源文件 arr 是把 處理 每次 fff

1. 概念簡介

  BufferedInputStream和BufferedOutputStream是帶緩沖區的字節輸入輸出處理流。它們本身並不具有IO流的讀取與寫入功能,只是在別的流(節點流或其他處理流)上加上緩沖功能提高效率,就像是把別的流包裝起來一樣,因此緩沖流是一種處理流。事實上,這兩個處理流(BufferedInputStream和 BufferedOutputStream),加上BufferedReader和BufferedWriter,這四個流在設計時使用到的正是裝飾設計模式通過裝飾設計模式,得以在其他的流的基礎上增加緩沖的功能

  當對文件或者其他數據源進行頻繁的讀寫操作時,效率比較低,這時如果使用緩沖流就能夠更高效的讀寫信息。因為緩沖流是先將數據緩存起來,然後當緩存區存滿後或者手動刷新時再一次性的讀取到程序或寫入目的地。

2. 使用FileInputStream和FileOutputStream復制文件的原理圖

技術分享圖片

3. 使用BufferedInputStream和BufferedOutputStream賦值文件的原理圖

技術分享圖片

3.1 MyBos

  由源碼可知,BufferedOutputStream自身並沒有實現將數據寫入目的地的功能,真正的寫入功能其實還是由它所裝飾的字節輸出流os來實現的。通過緩沖區的緩沖作用,增加了內存與內存之間的交互,減少了內存與磁盤的直接交互,以此提升I/O的效率。

  下面是在參考了BufferedOutputStream的源碼之後,自己實現的緩沖字節輸出流,其原理

非常簡單,即:首先在MyBos內部包裝了一個真正具有將數據輸出到目的地功能的字節輸出流os;在MyBos中還定義了一個字節緩沖數組buf,默認大小為8K,當調用write(byte b)或write(byte[] buf, int off, int len)方法將數據寫出時,並不是直接將數據寫出到目的地,首選是先將數據寫到MyBos中自定義的緩沖區buf中,只有當緩沖區滿了或者手動刷新的時候,才會將緩沖區的數據一次性寫入到目的地。

自己實現的緩沖字節輸出流的代碼如下所示:

import java.io.IOException;
import java.io.OutputStream;

public
class MyBos { private byte[] buf; //緩沖字節數組 private int count; //記錄目前緩沖數組中的有效字節數 private OutputStream os;//被裝飾的底層輸出字節流 //構造方法 public MyBos(OutputStream os) throws Exception { this(os, 8192); //若不顯式指定緩沖數組的大小,則默認分配8k空間 } public MyBos(OutputStream os, int size) throws Exception { if(size < 0) { throw new Exception("緩沖區空間大小分配錯誤:" + size); } this.os = os; buf = new byte[size]; } /** * 寫出單個字節數據 * @param b 寫出的字節數據 * @throws IOException */ public void write(int b) throws IOException { if(count == buf.length) {//若count == buf.length,說明緩沖區buf已滿,則先將緩沖區數據寫出 os.write(buf); //調用os的wirte(byte[] buf)方法講數據寫出! count = 0; //重置count } buf[count++] = (byte)b; //將字節數據寫入緩沖區,寫入到count所在索引位置上 } /** * 將字節數組b[offset, offset+len)部分寫出 * @param b 字節數組 * @param offset 開始位置 * @param len 長度 * @throws Exception */ public void write(byte[] b, int offset, int len) throws Exception { if(offset < 0 || len < 0 || b.length < (offset + len)) { throw new Exception("數組索引越界異常!"); } if(len > buf.length) { //若寫出的字節數組b數據大於緩沖數組 flush(); //則先調用flush()方法將緩沖數組的數據寫出 os.write(b, offset, len); //直接將數組b的數據通過os的wirte方法寫出,不寫入緩沖數組了 return; } if(len > buf.length - count) { //若緩沖數組剩余空間不足len長度 flush(); //則先將緩沖數組的數據寫出 } System.arraycopy(b, offset, buf, count, len); count += len; } /** * 刷新緩沖區 * @throws IOException */ public void flush() throws IOException { if(count > 0) { //若緩沖區中有數據,則調用底層輸出流os將數據寫出 os.write(buf, 0, count); os.flush(); count = 0; //重置count } } /** * 關閉流 * @throws IOException */ public void close() throws IOException { if(null != os) { flush(); //先將緩沖數組的數據寫出 os.close(); //接著關閉底層數據流os } } }

3.2 MyBis

  由BufferedInputStream的源碼可知,該類的內部同樣有一個字節緩沖區buf,每次讀取數據時,它會先檢查緩沖區中是否有數據,若有則直接從緩沖區中取數,若是沒有,則先通過底層輸入流將指定大小(默認是8192個字節)的數據從底層讀取到緩沖區中,再從緩沖區拿數。

  跟BufferedOutputStream一樣,BufferedInputStream自身也沒有實現將數據從源文件讀入內存的功能,真正的讀取功能其實還是由它所裝飾的底層字節輸入流is來實現的。通過緩沖區的緩沖作用,增加了內存與內存之間的交互,減少了內存與磁盤的直接交互,以此提升I/O的效率。

自己實現的BufferedInputStream示例代碼:

import java.io.IOException;
import java.io.InputStream;

public class MyBis {
    //用於訪問底層數據的底層流
    private InputStream is;
    //字節數組緩沖區
    byte[] buf;
    //默認緩存大小8k
    public static final int DEFAULT_BUF_SIZE = 8192;
    //記錄緩沖區中 待讀取的字節數
    private int count;
    //用於記錄讀取到當前 buf 中字節數據的位置
    private int pos;
    
    //構造方法
    public MyBis(InputStream is) throws Exception {
        this(is, DEFAULT_BUF_SIZE);
    }

    public MyBis(InputStream is, int size) throws Exception {
        if(size < 0) {
            throw new Exception("\"緩沖區空間大小分配錯誤:\" + size");
        }
        this.is = is;
        buf = new byte[DEFAULT_BUF_SIZE];
    }
    
    
    /**
     * 從緩沖區中讀取下一個字節的數據,如果數據到到了末尾,返回 -1
     * @return 讀取到的字節數據的 int 形式,如果讀取到了流的末尾,返回 -1
     * @throws IOException 
     */
    public int read() throws IOException {
        if(pos == count) {//若讀取的下一個字節數已經到達有效數據的末尾
            count = is.read(buf);//則通過底層的輸入流,一次性讀取 8192 個字節數據到緩沖區中來。
            if(-1 == count) {//若is 沒有讀取到數據,直接返回-1
                return -1;
            }
            //若is讀取到數據了,將pos設置為第一個字節
            pos = 0;
        } 
        
        //返回讀取到的字節數據的 int 形式
        return buf[pos++] & 0xff;
    }
    
    
    /**
     * 處理流的關閉問題:只關閉處理流即可,處理流的關閉會將底層的流關閉掉。
     * @throws IOException
     */
    public void close() throws IOException {
        if(null != is) {
            is.close();
        }
    }
}

測試自己寫的字節緩沖輸入流:

import java.io.FileInputStream;
import java.io.IOException;

public class TestMyBis {
    public static void main(String[] args) {
        MyBis mb = null;
        
        try {
            mb = new MyBis(new FileInputStream("./src/res/1.txt"));
            
            int value = 0;
            
            while(-1 != (value = mb.read())) {
                System.out.print((char)value);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != mb) {
                try {
                    mb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

代碼運行效果:

技術分享圖片

4. 字節緩沖流應用示例

示例代碼:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedStreamTest {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        
        try {
            //使用緩沖流
            bis = new BufferedInputStream(new FileInputStream("e:/test_file/data_structure.avi"));
            bos = new BufferedOutputStream(new FileOutputStream("e:/test_file/data_structure_copy.avi"));
            int len = 0;
            byte[] buf = new byte[1024*1024];//因為讀取的源文件較大,所以這裏分配的空間大一點
            
            while(-1 != (len = bis.read(buf))) {
                bos.write(buf, 0, len);
            }
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != bos) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != bis) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
}

最後,由BufferedInputStream和BufferedOutputStream這兩個類的源碼或API可以看出,它們沒有新增任何新的方法,使用的都是常見的read()、read(byte[] b)、write(int b)、write(byte[] buf, int off, int len)等方法。

J05-Java IO流總結五 《 BufferedInputStream和BufferedOutputStream 》