Java NIO API詳解

JDK 1.4 以前, Java IO 操作集中在 java.io 這個包中,是基於流的同步( blocking API 。對於大多數應用來說,這樣的 API 使用很方便,然而,一些對性能要求較高的應用,尤其是服務端應用,往往需要一個更爲有效的方式來處理 IO 。從 JDK 1.4 起, NIO API 作爲一個基於緩衝區,並能提供異步 (non-blocking)IO 操作的 API 被引入。本文對其進行深入的介紹。

 

NIO API 主要集中在 java.nio 和它的 subpackages 中:

 

java.nio

定義了 Buffer 及其數據類型相關的子類。其中被 java.nio.channels 中的類用來進行 IO 操作的 ByteBuffer 的作用非常重要。

 

java.nio.channels

定義了一系列處理 IO Channel 接口以及這些接口在文件系統和網絡通訊上的實現。通過 Selector 這個類,還提供了進行異步 IO 操作的辦法。這個包可以說是 NIO API 的核心。

 

java.nio.channels.spi

定義了可用來實現 channel selector API 的抽象類。

 

java.nio.charset

       定義了處理字符編碼和解碼的類。

 

java.nio.charset.spi

       定義了可用來實現 charset API 的抽象類。

 

java.nio.channels.spi java.nio.charset.spi 這兩個包主要被用來對現有 NIO API 進行擴展,在實際的使用中,我們一般只和另外的 3 個包打交道。下面將對這 3 個包一一介紹。

 

Package java.nio

這個包主要定義了 Buffer 及其子類。 Buffer 定義了一個線性存放 primitive type 數據的容器接口。對於除 boolean 以外的其他 primitive type ,都有一個相應的 Buffer 子類, ByteBuffer 是其中最重要的一個子類。

 

下面這張 UML 類圖描述了 java.nio 中的類的關係:

 



 

Buffer

定義了一個可以線性存放 primitive type 數據的容器接口。 Buffer 主要包含了與類型( byte, char… )無關的功能。值得注意的是 Buffer 及其子類都不是線程安全的。

 

每個 Buffer 都有以下的屬性:

 

capacity

這個 Buffer 最多能放多少數據。 capacity 一般在 buffer 被創建的時候指定。

limit

Buffer 上進行的讀寫操作都不能越過這個下標。當寫數據到 buffer 中時, limit 一般和 capacity 相等,當讀數據時, limit 代表 buffer 中有效數據的長度。

position

/ 寫操作的當前下標。當使用 buffer 的相對位置進行讀 / 寫操作時,讀 / 寫會從這個下標進行,並在操作完成後, buffer 會更新下標的值。

mark

一個臨時存放的位置下標。調用 mark() 會將 mark 設爲當前的 position 的值,以後調用 reset() 會將 position 屬性設置爲 mark 的值。 mark 的值總是小於等於 position 的值,如果將 position 的值設的比 mark 小,當前的 mark 值會被拋棄掉。

 

這些屬性總是滿足以下條件:

0 <= mark <= position <= limit <= capacity

 

limit position 的值除了通過 limit() position() 函數來設置,也可以通過下面這些函數來改變:

 

Buffer clear()

position 設爲 0 ,把 limit 設爲 capacity ,一般在把數據寫入 Buffer 前調用。

Buffer flip()

limit 設爲當前 position ,把 position 設爲 0 ,一般在從 Buffer 讀出數據前調用。

Buffer rewind()

position 設爲 0 limit 不變,一般在把數據重寫入 Buffer 前調用。

 

Buffer 對象有可能是隻讀的,這時,任何對該對象的寫操作都會觸發一個 ReadOnlyBufferException isReadOnly() 方法可以用來判斷一個 Buffer 是否只讀。

 

ByteBuffer

Buffer 的子類中, ByteBuffer 是一個地位較爲特殊的類,因爲在 java.io.channels 中定義的各種 channel IO 操作基本上都是圍繞 ByteBuffer 展開的。

 

ByteBuffer 定義了 4 static 方法來做創建工作:

 

ByteBuffer allocate(int capacity)

創建一個指定 capacity ByteBuffer

ByteBuffer allocateDirect(int capacity)

創建一個 direct ByteBuffer ,這樣的 ByteBuffer 在參與 IO 操作時性能會更好(很有可能是在底層的實現使用了 DMA 技術),相應的,創建和回收 direct ByteBuffer 的代價也會高一些。 isDirect() 方法可以檢查一個 buffer 是否是 direct 的。

ByteBuffer wrap(byte [] array)

ByteBuffer wrap(byte [] array, int offset, int length)

把一個 byte 數組或 byte 數組的一部分包裝成 ByteBuffer

 

ByteBuffer 定義了一系列 get put 操作來從中讀寫 byte 數據,如下面幾個:

 

byte get()

ByteBuffer get(byte [] dst)

byte get(int index)

 

ByteBuffer put(byte b)

ByteBuffer put(byte [] src)

ByteBuffer put(int index, byte b)

 

這些操作可分爲絕對定位和相對定爲兩種,相對定位的讀寫操作依靠 position 來定位 Buffer 中的位置,並在操作完成後會更新 position 的值。

在其它類型的 buffer 中,也定義了相同的函數來讀寫數據,唯一不同的就是一些參數和返回值的類型。

 

除了讀寫 byte 類型數據的函數, ByteBuffer 的一個特別之處是它還定義了讀寫其它 primitive 數據的方法,如:

 

int getInt()

       ByteBuffer 中讀出一個 int 值。

ByteBuffer putInt(int value)

       寫入一個 int 值到 ByteBuffer 中。

 

讀寫其它類型的數據牽涉到字節序問題, ByteBuffer 會按其字節序(大字節序或小字節序)寫入或讀出一個其它類型的數據( int,long… )。字節序可以用 order 方法來取得和設置:

 

ByteOrder order()

       返回 ByteBuffer 的字節序。

ByteBuffer order(ByteOrder bo)

       設置 ByteBuffer 的字節序。

 

ByteBuffer 另一個特別的地方是可以在它的基礎上得到其它類型的 buffer 。如:

 

CharBuffer asCharBuffer()

爲當前的 ByteBuffer 創建一個 CharBuffer 的視圖。在該視圖 buffer 中的讀寫操作會按照 ByteBuffer 的字節序作用到 ByteBuffer 中的數據上。

 

用這類方法創建出來的 buffer 會從 ByteBuffer position 位置開始到 limit 位置結束,可以看作是這段數據的視圖。視圖 buffer readOnly 屬性和 direct 屬性與 ByteBuffer 的一致,而且也只有通過這種方法,纔可以得到其他數據類型的 direct buffer

 

ByteOrder

用來表示 ByteBuffer 字節序的類,可將其看成 java 中的 enum 類型。主要定義了下面幾個 static 方法和屬性:

 

ByteOrder BIG_ENDIAN

       代表大字節序的 ByteOrder

ByteOrder LITTLE_ENDIAN

       代表小字節序的 ByteOrder

ByteOrder nativeOrder()

       返回當前硬件平臺的字節序。

 

MappedByteBuffer

ByteBuffer 的子類,是文件內容在內存中的映射。這個類的實例需要通過 FileChannel map() 方法來創建。

 

 

接下來看看一個使用 ByteBuffer 的例子,這個例子從標準輸入不停地讀入字符,當讀滿一行後,將收集的字符寫到標準輸出:

    public static void main(String [] args)

       throws IOException

    {

       // 創建一個 capacity 256 ByteBuffer

       ByteBuffer buf = ByteBuffer.allocate(256);

       while ( true ) {

           // 從標準輸入流讀入一個字符

           int c = System.in.read();

           // 當讀到輸入流結束時,退出循環

           if (c == -1)

              break ;

          

           // 把讀入的字符寫入 ByteBuffer

           buf.put(( byte ) c);

           // 當讀完一行時,輸出收集的字符

           if (c == '/n' ) {

              // 調用 flip() 使 limit 變爲當前的 position 的值 ,position 變爲 0,

              // 爲接下來從 ByteBuffer 讀取做準備

              buf.flip();

              // 構建一個 byte 數組

              byte [] content = new byte [buf.limit()];

              // ByteBuffer 中讀取數據到 byte 數組中

              buf.get(content);

              // byte 數組的內容寫到標準輸出

              System.out.print( new String(content));

              // 調用 clear() 使 position 變爲 0,limit 變爲 capacity 的值,

              // 爲接下來寫入數據到 ByteBuffer 中做準備

              buf.clear();

           }

       }

    }

 

 

Package java.nio.channels

這個包定義了 Channel 的概念, Channel 表現了一個可以進行 IO 操作的通道(比如,通過 FileChannel ,我們可以對文件進行讀寫操作)。 java.nio.channels 包含了文件系統和網絡通訊相關的 channel 類。這個包通過 Selector SelectableChannel 這兩個類,還定義了一個進行異步( non-blocking IO 操作的 API ,這對需要高性能 IO 的應用非常重要。

 

下面這張 UML 類圖描述了 java.nio.channels interface 的關係:

 



 

Channel

Channel 表現了一個可以進行 IO 操作的通道,該 interface 定義了以下方法:

 

boolean isOpen()

       Channel 是否是打開的。

void close()

       關閉這個 Channel ,相關的資源會被釋放。

 

ReadableByteChannel

定義了一個可從中讀取 byte 數據的 channel interface

 

int read(ByteBuffer dst)

channel 中讀取 byte 數據並寫到 ByteBuffer 中。返回讀取的 byte 數。

 

WritableByteChannel

定義了一個可向其寫 byte 數據的 channel interface

 

int write(ByteBuffer src)

       ByteBuffer 中讀取 byte 數據並寫到 channel 中。返回寫出的 byte 數。

 

ByteChannel

ByteChannel 並沒有定義新的方法,它的作用只是把 ReadableByteChannel WritableByteChannel 合併在一起。

 

ScatteringByteChannel

繼承了 ReadableByteChannel 並提供了同時往幾個 ByteBuffer 中寫數據的能力。

 

GatheringByteChannel

繼承了 WritableByteChannel 並提供了同時從幾個 ByteBuffer 中讀數據的能力。

 

InterruptibleChannel

用來表現一個可以被異步關閉的 Channel 。這表現在兩方面:

1.    當一個 InterruptibleChannel close() 方法被調用時,其它 block 在這個 InterruptibleChannel IO 操作上的線程會接收到一個 AsynchronousCloseException

2.    當一個線程 block InterruptibleChannel IO 操作上時,另一個線程調用該線程的 interrupt() 方法會導致 channel 被關閉,該線程收到一個 ClosedByInterruptException ,同時線程的 interrupt 狀態會被設置。

 

 

接下來的這張 UML 類圖描述了 java.nio.channels 中類的關係:

 



 

異步 IO

異步 IO 的支持可以算是 NIO API 中最重要的功能,異步 IO 允許應用程序同時監控多個 channel 以提高性能,這一功能是通過 Selector SelectableChannel SelectionKey 3 個類來實現的。

 

SelectableChannel 代表了可以支持異步 IO 操作的 channel ,可以將其註冊在 Selector 上,這種註冊的關係由 SelectionKey 這個類來表現(見 UML 圖)。 Selector 這個類通過 select() 函數,給應用程序提供了一個可以同時監控多個 IO channel 的方法:

 

應用程序通過調用 select() 函數,讓 Selector 監控註冊在其上的多個 SelectableChannel ,當有 channel IO 操作可以進行時, select() 方法就會返回以讓應用程序檢查 channel 的狀態,並作相應的處理。

 

下面是 JDK 1.4 中異步 IO 的一個例子,這段 code 使用了異步 IO 實現了一個 time server

    private static void acceptConnections( int port) throws Exception {

       // 打開一個 Selector

       Selector acceptSelector =

           SelectorProvider.provider().openSelector();

 

       // 創建一個 ServerSocketChannel ,這是一個 SelectableChannel 的子類

       ServerSocketChannel ssc = ServerSocketChannel.open();

       // 將其設爲 non-blocking 狀態,這樣才能進行異步 IO 操作

       ssc.configureBlocking( false );

 

       // ServerSocketChannel 對應的 socket 綁定 IP 和端口

       InetAddress lh = InetAddress.getLocalHost();

       InetSocketAddress isa = new InetSocketAddress(lh, port);

       ssc.socket().bind(isa);

 

       // ServerSocketChannel 註冊到 Selector 上,返回對應的 SelectionKey

       SelectionKey acceptKey =

           ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);

 

       int keysAdded = 0;

 

       // select() 函數來監控註冊在 Selector 上的 SelectableChannel

       // 返回值代表了有多少 channel 可以進行 IO 操作 (ready for IO)

       while ((keysAdded = acceptSelector.select()) > 0) {

           // selectedKeys() 返回一個 SelectionKey 的集合,

           // 其中每個 SelectionKey 代表了一個可以進行 IO 操作的 channel

           // 一個 ServerSocketChannel 可以進行 IO 操作意味着有新的 TCP 連接連入了

           Set readyKeys = acceptSelector.selectedKeys();

           Iterator i = readyKeys.iterator();

 

           while (i.hasNext()) {

              SelectionKey sk = (SelectionKey) i.next();

              // 需要將處理過的 key selectedKeys 這個集合中刪除

              i.remove();

              // SelectionKey 得到對應的 channel

              ServerSocketChannel nextReady =

                  (ServerSocketChannel) sk.channel();

              // 接受新的 TCP 連接

              Socket s = nextReady.accept().socket();

              // 把當前的時間寫到這個新的 TCP 連接中

              PrintWriter out =

                  new PrintWriter(s.getOutputStream(), true );

              Date now = new Date();

              out.println(now);

              // 關閉連接

              out.close();

           }

       }

    }

這是個純粹用於演示的例子,因爲只有一個 ServerSocketChannel 需要監控,所以其實並不真的需要使用到異步 IO 。不過正因爲它的簡單,可以很容易地看清楚異步 IO 是如何工作的。

 

SelectableChannel

這個抽象類是所有支持異步 IO 操作的 channel (如 DatagramChannel SocketChannel )的父類。 SelectableChannel 可以註冊到一個或多個 Selector 上以進行異步 IO 操作。

 

SelectableChannel 可以是 blocking non-blocking 模式(所有 channel 創建的時候都是 blocking 模式),只有 non-blocking SelectableChannel 纔可以參與異步 IO 操作。

 

SelectableChannel configureBlocking(boolean block)

       設置 blocking 模式。

boolean isBlocking()

       返回 blocking 模式。

 

通過 register() 方法, SelectableChannel 可以註冊到 Selector 上。

 

int validOps()

返回一個 bit mask ,表示這個 channel 上支持的 IO 操作。當前在 SelectionKey 中,用靜態常量定義了 4 IO 操作的 bit 值: OP_ACCEPT OP_CONNECT OP_READ OP_WRITE

SelectionKey register(Selector sel, int ops)

將當前 channel 註冊到一個 Selector 上並返回對應的 SelectionKey 。在這以後,通過調用 Selector select() 函數就可以監控這個 channel ops 這個參數是一個 bit mask ,代表了需要監控的 IO 操作。

SelectionKey register(Selector sel, int ops, Object att)

這個函數和上一個的意義一樣,多出來的 att 參數會作爲 attachment 被存放在返回的 SelectionKey 中,這在需要存放一些 session state 的時候非常有用。

boolean isRegistered()

       channel 是否已註冊在一個或多個 Selector 上。

 

SelectableChannel 還提供了得到對應 SelectionKey 的方法:

 

SelectionKey keyFor(Selector sel)

返回該 channe Selector 上的註冊關係所對應的 SelectionKey 。若無註冊關係,返回 null

 

Selector

Selector 可以同時監控多個 SelectableChannel IO 狀況,是異步 IO 的核心。

 

Selector open()

       Selector 的一個靜態方法,用於創建實例。

 

在一個 Selector 中,有 3 SelectionKey 的集合:

1. key set 代表了所有註冊在這個 Selector 上的 channel ,這個集合可以通過 keys() 方法拿到。

2. Selected-key set 代表了所有通過 select() 方法監測到可以進行 IO 操作的 channel ,這個集合可以通過 selectedKeys() 拿到。

3. Cancelled-key set 代表了已經 cancel 了註冊關係的 channel ,在下一個 select() 操作中,這些 channel 對應的 SelectionKey 會從 key set cancelled-key set 中移走。這個集合無法直接訪問。

 

以下是 select() 相關方法的說明:

 

int select()

監控所有註冊的 channel ,當其中有註冊的 IO 操作可以進行時,該函數返回,並將對應的 SelectionKey 加入 selected-key set

int select(long timeout)

       可以設置超時的 select() 操作。

int selectNow()

       進行一個立即返回的 select() 操作。

Selector wakeup()

       使一個還未返回的 select() 操作立刻返回。

 

SelectionKey

代表了 Selector SelectableChannel 的註冊關係。

 

Selector 定義了 4 個靜態常量來表示 4 IO 操作,這些常量可以進行位操作組合成一個 bit mask

 

int OP_ACCEPT

有新的網絡連接可以 accept ServerSocketChannel 支持這一異步 IO

int OP_CONNECT

       代表連接已經建立(或出錯), SocketChannel 支持這一異步 IO

int OP_READ

int OP_WRITE

       代表了讀、寫操作。

 

以下是其主要方法:

 

Object attachment()

返回 SelectionKey attachment attachment 可以在註冊 channel 的時候指定。

Object attach(Object ob)

       設置 SelectionKey attachment

SelectableChannel channel()

       返回該 SelectionKey 對應的 channel

Selector selector()

       返回該 SelectionKey 對應的 Selector

void cancel()

       cancel 這個 SelectionKey 所對應的註冊關係。

int interestOps()

       返回代表需要 Selector 監控的 IO 操作的 bit mask

SelectionKey interestOps(int ops)

       設置 interestOps

int readyOps()

       返回一個 bit mask ,代表在相應 channel 上可以進行的 IO 操作。

 

ServerSocketChannel

支持異步操作,對應於 java.net.ServerSocket 這個類,提供了 TCP 協議 IO 接口,支持 OP_ACCEPT 操作。

 

ServerSocket socket()

       返回對應的 ServerSocket 對象。

SocketChannel accept()

       接受一個連接,返回代表這個連接的 SocketChannel 對象。

 

SocketChannel

支持異步操作,對應於 java.net.Socket 這個類,提供了 TCP 協議 IO 接口,支持 OP_CONNECT OP_READ OP_WRITE 操作。這個類還實現了 ByteChannel ScatteringByteChannel GatheringByteChannel 接口。

DatagramChannel 和這個類比較相似,其對應於 java.net.DatagramSocket ,提供了 UDP 協議 IO 接口。

 

Socket socket()

       返回對應的 Socket 對象。

boolean connect(SocketAddress remote)

boolean finishConnect()

connect() 進行一個連接操作。如果當前 SocketChannel blocking 模式,這個函數會等到連接操作完成或錯誤發生才返回。如果當前 SocketChannel non-blocking 模式,函數在連接能立刻被建立時返回 true ,否則函數返回 false ,應用程序需要在以後用 finishConnect() 方法來完成連接操作。

 

Pipe

包含了一個讀和一個寫的 channel(Pipe.SourceChannel Pipe.SinkChannel) ,這對 channel 可以用於進程中的通訊。

 

FileChannel

用於對文件的讀、寫、映射、鎖定等操作。和映射操作相關的類有 FileChannel.MapMode ,和鎖定操作相關的類有 FileLock 。值得注意的是 FileChannel 並不支持異步操作。

 

Channels

這個類提供了一系列 static 方法來支持 stream 類和 channel 類之間的互操作。這些方法可以將 channel 類包裝爲 stream 類,比如,將 ReadableByteChannel 包裝爲 InputStream Reader ;也可以將 stream 類包裝爲 channel 類,比如,將 OutputStream 包裝爲 WritableByteChannel

 

 

Package java.nio.charset

這個包定義了 Charset 及相應的 encoder decoder 。下面這張 UML 類圖描述了這個包中類的關係,可以將其中 Charset CharsetDecoder CharsetEncoder 理解成一個 Abstract Factory 模式的實現:

 



 

Charset

代表了一個字符集,同時提供了 factory method 來構建相應的 CharsetDecoder CharsetEncoder

 

Charset 提供了以下 static 的方法:

 

SortedMap availableCharsets()

       返回當前系統支持的所有 Charset 對象,用 charset 的名字作爲 set key

boolean isSupported(String charsetName)

       判斷該名字對應的字符集是否被當前系統支持。

Charset forName(String charsetName)

       返回該名字對應的 Charset 對象。

 

Charset 中比較重要的方法有:

 

String name()

       返回該字符集的規範名。

Set aliases()

       返回該字符集的所有別名。

CharsetDecoder newDecoder()

       創建一個對應於這個 Charset decoder

CharsetEncoder newEncoder()

       創建一個對應於這個 Charset encoder

 

CharsetDecoder

將按某種字符集編碼的字節流解碼爲 unicode 字符數據的引擎。

 

CharsetDecoder 的輸入是 ByteBuffer ,輸出是 CharBuffer 。進行 decode 操作時一般按如下步驟進行:

 

1. 調用 CharsetDecoder reset() 方法。(第一次使用時可不調用)

2. 調用 decode() 方法 0 n 次,將 endOfInput 參數設爲 false ,告訴 decoder 有可能還有新的數據送入。

3. 調用 decode() 方法最後一次,將 endOfInput 參數設爲 true ,告訴 decoder 所有數據都已經送入。

4. 調用 decoder flush() 方法。讓 decoder 有機會把一些內部狀態寫到輸出的 CharBuffer 中。

 

CharsetDecoder reset()

       重置 decoder ,並清除 decoder 中的一些內部狀態。

CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput)

ByteBuffer 類型的輸入中 decode 儘可能多的字節,並將結果寫到 CharBuffer 類型的輸出中。根據 decode 的結果,可能返回 3 CoderResult CoderResult.UNDERFLOW 表示已經沒有輸入可以 decode CoderResult.OVERFLOW 表示輸出已滿;其它的 CoderResult 表示 decode 過程中有錯誤發生。根據返回的結果,應用程序可以採取相應的措施,比如,增加輸入,清除輸出等等,然後再次調用 decode() 方法。

CoderResult flush(CharBuffer out)

有些 decoder 會在 decode 的過程中保留一些內部狀態,調用這個方法讓這些 decoder 有機會將這些內部狀態寫到輸出的 CharBuffer 中。調用成功返回 CoderResult.UNDERFLOW 。如果輸出的空間不夠,該函數返回 CoderResult.OVERFLOW ,這時應用程序應該擴大輸出 CharBuffer 的空間,然後再次調用該方法。

CharBuffer decode(ByteBuffer in)

一個便捷的方法把 ByteBuffer 中的內容 decode 到一個新創建的 CharBuffer 中。在這個方法中包括了前面提到的 4 個步驟,所以不能和前 3 個函數一起使用。

 

decode 過程中的錯誤有兩種: malformed-input CoderResult 表示輸入中數據有誤; unmappable-character CoderResult 表示輸入中有數據無法被解碼成 unicode 的字符。如何處理 decode 過程中的錯誤取決於 decoder 的設置。對於這兩種錯誤, decoder 可以通過 CodingErrorAction 設置成:

1. 忽略錯誤

2. 報告錯誤。(這會導致錯誤發生時, decode() 方法返回一個表示該錯誤的 CoderResult 。)

3. 替換錯誤,用 decoder 中的替換字串替換掉有錯誤的部分。

 

CodingErrorAction malformedInputAction()

       返回 malformed-input 的出錯處理。

CharsetDecoder onMalformedInput(CodingErrorAction newAction)

       設置 malformed-input 的出錯處理。

CodingErrorAction unmappableCharacterAction()

       返回 unmappable-character 的出錯處理。

CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction)

       設置 unmappable-character 的出錯處理。

String replacement()

       返回 decoder 的替換字串。

CharsetDecoder replaceWith(String newReplacement)

       設置 decoder 的替換字串。

 

CharsetEncoder

unicode 字符數據編碼爲特定字符集的字節流的引擎。其接口和 CharsetDecoder 相類似。

 

CoderResult

描述 encode/decode 操作結果的類。

 

CodeResult 包含兩個 static 成員:

 

CoderResult OVERFLOW

       表示輸出已滿

CoderResult UNDERFLOW

       表示輸入已無數據可用。

 

其主要的成員函數有:

 

boolean isError()

boolean isMalformed()

boolean isUnmappable()

boolean isOverflow()

boolean isUnderflow()

       用於判斷該 CoderResult 描述的錯誤。

 

int length()

       返回錯誤的長度,比如,無法被轉換成 unicode 的字節長度。

void throwException()

       拋出一個和這個 CoderResult 相對應的 exception

 

CodingErrorAction

表示 encoder/decoder 中錯誤處理方法的類。可將其看成一個 enum 類型。有以下 static 屬性:

 

CodingErrorAction IGNORE

       忽略錯誤。

CodingErrorAction REPLACE

       用替換字串替換有錯誤的部分。

CodingErrorAction REPORT

報告錯誤,對於不同的函數,有可能是返回一個和錯誤有關的 CoderResult ,也有可能是拋出一個 CharacterCodingException

 

 

參考文獻

David Flanagan – Java in a Nutshell


作者: DaiJiaLin                        

mailto:[email protected]

http://blog.csdn.net/DaiJiaLin


發佈了43 篇原創文章 · 獲贊 3 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章