JavaIO :JavaIO輸入輸出流

流的原理:

clipboard.png

一連串有順序的數據系列能夠當作是一個流。java

輸入輸出流:

數據從IO輸入到程序的流是輸入流,數據從程序輸出到IO的流是輸出流。數組

下面咱們使用字節輸入輸出流來講明這個問題:app

  • 輸入流 InputStream通常是由javaio對象(如File)創建的,當新建一個InputStream時,io對象創建了一個包含有數據的管道(其實就是咱們所說的這個「流」)並將io對象存儲的數據輸入(input)到管道中,所以管道里的數據流就是io對象內的數據。當調用InputStream數據流的read方法時,管道里的數據流讀出到內存對象中(好比數組或者字符串),注意,讀出的比特流將會被移除,具體能可讀的數據的量可用available函數來查看;函數

  • 輸出流 OutputStream也是由javaio對象(如File)創建的,當新建一個OutputStream時,io對象創建了一個包含有數據流的管道並創建起io對象和管道的映射。 當調用OutputStream數據流的write方法時,內存對象裏的數據就會流入管道里,而管道里的數據流輸出(output)到io對象中,flush函數將促使數據緩衝區中的數據被寫入到io設備(文件自己)中區;性能

clipboard.png

public class FilesTest {
    public static void main(String[] args) throws IOException {
       File file = new File(「1.txt");
       InputStream inputStream = new FileInputStream(file);
       byte[] buffer = new byte[1024];
       while(inputStream.read(buffer)!=-1)
        System.out.println(new String(buffer));
       inputStream.close();
    }
}

在這個例子裏咱們能夠充分看出輸入流建立和輸入的過程,首先建立一個File對象來映射IO上的這個文件,依據這個File對象來建立輸入流InputStream對象,注意,建立事後輸入流裏按序存儲着IO文件裏的數據內容(這個過程當中可能InputStream並非其存儲做用的,由於若果這樣大文件內的數據一次性存儲可能會爆內存,因此這個過程應該是InputStream映射到IO文件),調用輸入流InputStream對象的read方法,便可將流內的數據輸入到程序中的以前建立的對象內,最終在使用完後關閉做爲有限資源的輸入流。這個過程完成了數據由IO對象輸入到程序。spa

注意:若是是上次沒有讀完輸入流內的內容,那麼下一次程序到InputStream去讀的時候是接着上次的結尾讀的,這個能夠根據InputStream對象的available方法看出來,因此在這個角度來看輸入流就像是文件裏的索引指針同樣。指針

clipboard.png

public class FilesTest {
        public static void main(String[] args) throws IOException {
           File file = new File("test.txt");
           OutputStream outputStream = new FileOutputStream(file);
           byte[] buffer = "hello".getBytes();
           outputStream.write(buffer);
           outputStream.close();
        }
    }

在這個例子裏咱們能夠充分看出輸出流建立和輸出的過程,首先建立一個File對象來映射IO上的這個文件,依據這個File對象來建立輸出流OutputStream對象,調用輸出流OutputStream對象的write方法,便可將程序對象中的數據寫到輸出流中而後從輸出流輸出到IO文件中去,最終在使用完後關閉做爲有限資源的輸出流。這個過程完成了數據由程序輸出到IO文件中。code

字符輸入輸出流的道理是同樣的,只不過字符流是直接處理字符的,而字節流的處理單位是字節。
read和write的API大同小異,無非就是把流裏面的內容和緩衝區經過這些函數來進行交換。對象

流的流:

既然能夠依據IO文件來建立流在文件和程序之間交換數據,那麼咱們可不能夠從中間再加入一個流來做爲中轉處理一下數據呢?這個流的流構成的多流鏈稱之爲「流對象鏈」,這個過程說明不是全部的流都是直接和原始數據源打交道的,因此有以下定義:索引

節點流(Node Stream)直接鏈接到數據源,直接從IO文件上輸入或輸出數據;
處理流(Processing Stream)是對一個已存在的流的鏈接和封裝,經過所封裝的流的功能調用實現加強的數 據讀寫功能,它並不直接連到數據源。

public class FilesTest {
    public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("1.txt");
        PrintStream printStream = new PrintStream(outputStream);
        printStream.print("xxx");
        printStream.close();
        outputStream.close();
    }
}

這個過程的原理以下圖所示:

clipboard.png

在這個例子裏咱們能夠充分看出流對象鏈造成的過程,首先建立一個File對象來映射IO上的這個文件,依據這個File對象來建立輸出流OutputStream對象,利用這個輸出流對象再建立一個PrintStream對象來連接輸出流對象,調用PrintStream對象的print方法,便可將程序對象中的數據寫到PrintStream中而後再輸出到IO文件中去,最終在使用完後關閉做爲有限資源的流。這個過程完成了數據由程序輸出到IO文件中。

流中的緩衝技術

在內存中開闢一塊區域,稱爲緩衝區,當緩衝區滿時一次寫入到磁盤中,提升了I/O的性能。和通常的輸入輸出流相比,這樣的帶有緩衝區的流能夠作到更好的IO性能,在帶緩衝的輸出流時因爲緩衝區的存在,須要在最後強制使用flush函數將緩衝區中剩餘的內容所有輸出到IO設備中去。

clipboard.png

public class FilesTest {
    public static void main(String[] args) {
        try {
            byte[] data = new byte[1];
            File srcFile = new File("1.txt");
            File desFile = new File("2.txt");
            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(desFile));
            while (bufferedInputStream.read(data) != -1) {
                bufferedOutputStream.write(data);
            }
            bufferedOutputStream.flush();
            bufferedInputStream.close();
            bufferedOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

這裏表現的是這個過程,IO文件裏的數據通過文件輸入流FileInputStream對象流入緩衝輸入流BufferedInputStream對象,以後全部的數據(在數據量較小的時候,通常是小於8K時,後面會討論到)流入輸入流的緩衝區,以後每次在讀取的時候都是直接從緩衝區讀到臨時的數組中去而不是再從流讀入,而後臨時數組的數據在write函數的做用下寫到輸出流的緩衝區中去,緩衝區滿後數據會經由緩衝輸出流BufferedOutputStream對象流入文件輸出流FileOutputStream對象,並最終輸出到IO文件中去,若是緩衝區不滿的話是不會自發輸出到緩衝輸出流中去的,所以每每咱們須要在最後緩衝區不滿的狀況下強制執行輸出流的flush方法讓緩衝區數據強制輸出到輸出流中去。這個過程完成了IO文件數據的流轉,中間有一個緩衝區在暫存數據。

public class FilesTest {
    public byte[] generateString() {
        StringBuffer buffer = new StringBuffer();
        String content = "abcdefg\r\n";
        for (int i = 0; i < 10000; i++) {
            buffer.append(content);
        }
        return buffer.toString().getBytes();
    }

    public static void main(String[] args) throws IOException {
        FilesTest filesTest = new FilesTest();
        byte[] buffer = filesTest.generateString();
        InputStream inputStream = new ByteArrayInputStream(buffer);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        bufferedReader.readLine();
        System.out.println(dataInputStream.readline());
    }
}

在這個例子裏咱們能夠更容易地發現BufferedReader這樣的緩衝類輸入流的緩衝做用,當首次調用readline(或者read等各類讀取方法)函數讀取這個輸入流的時候,就會將流裏的數據讀進程序爲BufferedInputStream對象分配的一個緩衝區中,而在此後的讀取輸入流的過程當中就不須要去流中讀取而只須要去緩衝區裏讀取就能夠了,將開銷較大的IO數據交換過程變成了開銷小得多的內存數據交換,進而提升了IO效率,這是緩衝輸入輸出流的好處。可是這個緩衝區的大小是有限的,jdk爲這個大小肯定的固定值爲8K字節,一旦超過這個值的話在第一次讀取時就只能緩衝最多8K子節的數據,超出的部分只能在以後再緩衝。最後,若是要結束任務寫入輸出流的時候,要注意調用輸出流的flush方法來將緩衝區強制清空使之所有輸出到輸出流中去。

public class FilesTest {
    public static void main(String[] args) throws IOException {
        //buffer:8192    
        File src = new File("1.txt");
        File des = new File("2.txt");
        FileWriter writer = new FileWriter(src);
        StringBuffer buffer = new StringBuffer();
        for(int i = 0;i<8193;i++){
            buffer.append("a");
        }
        writer.write(buffer.toString());
        writer.flush();
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(src));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(des));
        byte[] eachBuffer = new byte[1024];
        int haveRead;
        while((haveRead = bufferedInputStream.read(eachBuffer))!=-1){
            bufferedOutputStream.write(eachBuffer);
        }
        //bufferedOutputStream.flush();
    }
}

上面這個過程演示了緩衝區的大小,當輸入流的內容填不滿緩衝區時(也就是不足8192字節時),若是不用flush沒有辦法自動寫入文件,當原來緩衝區的大小大於這個值的時候,會一次性把上次的8192字節自動寫入,下一次會再讀入8192個字節,完成上面的過程。所以,這提醒咱們,使用帶有緩衝的輸出流時務必要在最後強制清空緩衝進入輸出流才能保證數據不出錯。

相關文章
相關標籤/搜索