java中的IO流總結(一)

在java API中,可以從其中讀入一個字節序列的對象叫做輸入流,而可以向其中寫入一個字節序列的對象叫做輸出流,這些字節序列的來源和目的地可以是文件,而且通常是文件,但是也可以是網絡連接,甚至還可以是內存塊,而抽象類InputStream和OutputStream構成了輸入/輸出(I/O)類層次結構的基礎。
因爲面向字節的流不便於處理以Unicode形式存儲的信息(Unicode中每個字符都使用了多個字節來表示),所以從抽象類Reader和Writer中繼承出來了一個專門用於處理Unicode字符的單獨的類層次結構,這些類擁有的讀寫操作都是基於兩字節的Unicode碼元的,而不是基於單字節的字符。

順便提一下,UTF-8編碼方式是一種8位的Unicode字符集,長度是可變的,一個字符可能是1個字節,2個字節,3個字節或者4個字節,中文一般是3個字節。
UCS-2編碼是固定長度爲16位的Unicode字符集,每個字符都佔2個字節。
UTF-16編碼也是一種16位的編碼字符集,在UTF-16中,字符要麼是2字節,要麼是4字節。

一、流
輸入流和輸出流的層次結構

                                    圖1-1 輸入流和輸出流的層次結構

這裏寫圖片描述

                                    圖1-2 Reader和Writer的層次結構

上圖1-1和1-2就是java的流家族,包含各種流類型
我們把流家族中的成員按照他們的使用方法來進行劃分,這樣就形成了處理字節和字符的兩個單獨的層次結構:
(1)對於單個的字節或字節數組,我們可以使用InputStream和OutputStream,他們可以可以讀寫單個的字節或字節數組
(2)對於字符串和數字:我們就需要功能更爲強大的子類,例如DataInputStream和DataOutputStream可以以二進制的格式讀取所有的基本java類型
(3)其他流:ZipInputStream和ZipOutputStream可以以常見的ZIP壓縮格式讀取文件

java.io.InputStream:
       abstract int read() :從數據中讀入一個字節,並返回該字節,這個read方法在碰到流的結尾時返回-1。
       int read(byte[] b) :讀入一個字節數組,並返回實際讀入的字節數,或者在碰到流的結尾時返回-1;這個read方法最多讀入b.length個字節
       int read(byte[] b, int off, int len) :讀入一個字節數組,這個read方法返回實際讀入的字節數,或者在碰到流的結尾時返回-1;

        參數:b:   數據讀入的數組
             off:   第一個讀入字節應該被放置的位置在b中的偏移量 
             len:   讀入字節的最大數量

       long skip(long n):在輸入流中跳過n個字節,返回實際跳過的字節數(如果碰到流的結尾,則可能小於n)
       int available( ):返回在不阻塞的情況下可獲取的字節數
        void close( ):關閉這個輸入流
       void mark( int readlimit):在輸入流的當前位置打一個標記(並非所有的流都支持這個特性),如果從輸入流中已經讀入的字節多餘readlimit個,則這個流允許忽略這個標記
       void reset( ):返回到最後一個標記,隨後對read的調用將重新讀入這些字節,如果當前沒有任何標記,則這個流不被重置。
       boolean markSupported( ):如果這個流支持打標記,則返回true

java.io.OutputStream:
       abstract void write(int n):寫出一個字節數據
       void write(byte[] b)
       void write(byte[] b,int off, int len):寫出所有字節或者某個範圍的字節到數組b

參數:b:   數據寫出的數組
     off:   第一個寫出字節在b中的偏移量 
     len:   寫出字節的最大數量

       void close( ):沖刷並關閉輸出流
       void flush( ):沖刷輸出流,也就是將所有緩衝的數據發送到目的地

首先,read和write方法在執行時都將阻塞,直至字節確實被讀入或者寫出,當完成對流的讀寫時,應該調用close方法關閉它,這個調用會釋放掉十分有限的操作系統資源,如果一個應用程序打開了過多的流而沒有關閉,系統的資源將被耗盡,關閉一個輸出流的同時還會沖刷用於該輸出流的緩衝區:所有被臨時置於緩衝區中,以便使用更大的包的形式傳遞的字符在關閉輸出流時都將被送出,但是如果不關閉文件,那麼寫出字節的最後一個包可能將永遠也得不到傳遞,所以就需要我們的flush方法來認爲的沖刷這些輸出
我們再來看一張圖
這裏寫圖片描述
這裏有四個附加接口:Closeable、Flushable、Readable和Appendable。
Closeable和Flushable非常簡單,分別擁有下面的兩個方法:
       void close( ) throws IOException
       void flush( )
InputStream、OutPutStream、Reader和Writer都實現了Closeable接口
而OutputStream和Writer還實現了Flushable接口

Readable接口只有一個方法:int read(CharBuffer cb)
CharBuffer 類擁有按順序和隨機的進行讀寫的方法,它表示內存中的一個緩衝區或者一個內存映像的文件

Appendable接口有兩個用於添加單個字符和字符序列的方法:
Appendable append(char c)
Appendable append(CharSequence s)
CharSequence 接口描述了一個char值序列的基本屬性,String,CharBuffer,StringBuilder和StringBuffer都實現了它
在流類家族中,只有Writer類實現了Appendable 接口

java.io.Closeable:
        void close( ):關閉這個Closeable,可能胡拋出IOException

java.io.Flushable:
        void flush( ):沖刷這個Flushable

java.lang.Readable:
        int read(CharBuffer cb ):嘗試着向cb讀入其可持有數量的char值,返回讀入的char值得數量,或者當從這個Readable中無法在獲取更多的值時返回-1。

java.lang.Appendable
        Appendable append(char c)
        Appendable append(CharSequence s)
向這個Appendable中追加給定的碼元或者給定的字符序列中的所有碼元,返回this

java.lang.CharSequence
        char charAt(int index):返回給定索引處的碼元
        int length( ):返回在這個序列中的碼元的數量
        CharSequence subSequence(int startIndex, int endIndex ):返回由存儲在startIndex到endIndex -1處的所有碼元構成的CharSequence
        String toString( ):返回這個序列中所有碼元構成的字符串

二、組合流過濾器:
FileInputStream和FileOutputStream可以提供一個附着在磁盤上的輸入流和輸出流,而我們只需要向它的構造器提供文件名或者文件的完整路徑。例如

FileInputStream fin = new FileInputStream("employee.dat");

這句代碼可以用戶目錄下的employee.dat文件
與抽象類InputStream 和OutputStream一樣,這些類只能在字節級別上進行讀寫,也就是說,我們只能從fin對象讀取字節或字節數組。

byte b = (byte)fin.read();

如果我們只有DataInputStream,我們就只能讀入數值類型:

DataInputStream din = ...;
double s = din.readDouble();

FileInputStream 中沒有任何讀入數值類型的方法,DataInputStream 中沒有任何從文件獲取數據的方法。

這是由於java使用了一種靈巧的機制來分離這兩種職責,某些流可以從文件和其他外部的位置上獲取字節(例如FileInputStream 和openStream),而某些流可以將字節組裝到更有用的數據類型中(例如DataInputStream 和PrintWriter),我們必須對二者進行結合。

例如,我們想從文件中讀入數字,需要創建一個FileInputStream ,然後將其傳遞給DataInputStream 構造器:

FileInputStream  fin = new FileInputStream("employee.dat");
DataInputStream  din = new DataInputStream(fin);
double s = din.readDouble();

又例如:流在默認的情況下是不能被緩衝區緩存的,也就是說每個對read的調用都會請求操作系統在分發一個字節。相比之下,我們可以請求一個數據塊存並將其放到緩衝區中,這樣每次讀取字節都是在緩存中讀取,會顯得更加高效,下面就是使用緩衝機制的構造器序列:

DataInputStream din = new DataInputStream (
    new BufferedInputStream(
         new FileInputStream("employee.dat")));

再例如:當多個流鏈接在一起的時候,我們需要跟蹤各個中介流,例如,當讀入輸入時,我們經常會要瀏覽下一個字節,以便於判斷是不是我們想要的值:

PushbackInputStream pbin = new PushbackInputStream(
    new BufferedInputStream(
         new FileInputStream("employee.dat")));

現在我們可以預讀下一個字節

int b = pbin.read();

如果下一個字節並不是我們所期望的,我們可以將其推回到流中

if(b != '<') pbin.unread(b);

但是對於讀入和推回是應用於可回推輸入流(PushbackInputStream)
如果你想能夠預先瀏覽並且還可以讀入數字,那就需要再結合DataInputStream 。

java的流類庫相比其他的編程語言有一點麻煩,其他語言諸如緩衝機制和預覽等細節都是自動處理的,但是java必須將多個流過濾器組合起來,不過呢,這種混合並匹配過濾器類以構建真正有用的流序列能力,將帶來極大的靈活性。

java.io.FileInputStream
        FileInputStream(String name)
        FileInputStream(File file)
使用由name字符串或者file對象指定路徑名的文件,創建一個新的文件輸入流,非絕對路徑名將按照相對於VM啓動時所設置的相對目錄解析

java.io.FileOutputStream
        FileOutputStream(String name)
        FileOutputStream(String name,boolean append)
        FileOutputStream(File file)
        FileOutputStream(File file,boolean append)
使用由name字符串或者file對象指定路徑名的文件創建一個新的輸出流,如果append爲true,那麼數據將被添加到文件尾部,而具有相同名字的已有文件不會刪除;否則,這個方法刪除所有相同名字的已有文件

java.io.BufferedInputStream
        BufferedInputStream(InputStream in)
創建一個帶緩衝區的流,帶緩衝區的輸入流在從流中讀入字符時,不會每次都對設備訪問,而是從緩存中讀取。當緩衝區爲空時,會向緩衝區中讀入一個新的數據塊

java.io.OutputStream
        BufferedInputStream(OutputStream out)
創建一個帶緩衝區的流,帶緩衝區的輸出流在收集要寫出的字符時,不會每次都對設備訪問。當緩衝區填滿或當流被沖刷時,數據就被寫出。

java.io.PushbackInputStream
        PushbackInputStream(InputStream in)
        PushbackInputStream(InputStream in,int size)
構建一個可以預覽一個字節或者具有指定大小的回推緩衝區的流
        void unread(int b)
回推一個字節,它可以在下次調用read方法是被再次獲取
參數 :b 要再次讀入的字節

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章