// 此段代碼功能為從 t.txt 里復(fù)制所有數(shù)據(jù)到 out_j.txt:
//
...
1 FileChannel fcin = new FileInputStream( "d:/t.txt" ).getChannel();
2 FileChannel fcout = new FileOutputStream( new File( "d:/out_j.txt" )).getChannel();
3 ByteBuffer buff = ByteBuffer.allocate( 1024 );
4 long t1 = System.currentTimeMillis();
5
6 while( fcin.read( buff ) != 1 )
7 {
8 buff.flip();
9 fcout.write( buff );
10 buff.clear();
11 }
12
13 long t2 = System.currentTimeMillis();
14 long size = fcin.size();
15 javax.swing.JOptionPane.showMessageDialog( null, size + " 字節(jié), 耗時(shí) " + ( t2 t1 + 1 ) + " ms." );
...
SDK 文檔里對(duì) ByteBuffer 的說(shuō)明為:
public abstract class ByteBuffer
extends Buffer
implements Comparable
這說(shuō)明 ByteBuffer 是繼承于 Buffer 的抽象類(lèi), 實(shí)現(xiàn)了兩個(gè)接口.
行3 通過(guò) allocate() 分配了一塊 1024 字節(jié)的緩沖區(qū), 并返回一個(gè) ByteBuffer 對(duì)象. (抽象類(lèi)不能直接 new)
行6 fcin.read() 將數(shù)據(jù)讀入到 buff. 此處的 read() 是 FileChannel 類(lèi)的一個(gè)虛函數(shù).
行8 buff.flip() 這個(gè)調(diào)用就是開(kāi)頭一直無(wú)法理解的部分.
SDK 文檔里的對(duì) flip() 的說(shuō)明是:
public final Buffer flip()
反轉(zhuǎn)此緩沖區(qū)。首先對(duì)當(dāng)前位置設(shè)置限制,然后將該位置設(shè)置為零。如果已定義了標(biāo)記,則丟棄該標(biāo)記。
當(dāng)將數(shù)據(jù)從一個(gè)地方傳輸?shù)搅硪粋€(gè)地方時(shí),經(jīng)常將此方法與 compact 方法一起使用。
我最終的理解是: 文檔翻譯得太差了, 把不應(yīng)該翻譯的內(nèi)容也譯成了中文, 所以反而不容易理解.
關(guān)鍵就在以下 2 處:
當(dāng)前位置: 這個(gè)可以直觀地理解為緩沖區(qū)中的當(dāng)前數(shù)據(jù)指針, 或是 SQL 中的游標(biāo), 記為 curPointer.
限制: 這個(gè)可以理解成實(shí)際操作的緩沖區(qū)段的結(jié)束標(biāo)記, 記為 endPointer.
反轉(zhuǎn): 這個(gè)完全是對(duì) flip 這個(gè)詞不負(fù)責(zé)的翻譯, 如果參照 DirectX 里的 flip() 而譯為翻轉(zhuǎn)/翻頁(yè), 那就好理解得多, 就像寫(xiě)信/看信, 寫(xiě)/看完一頁(yè)后, 翻到下一頁(yè), 眼睛/筆從頁(yè)底重新移回頁(yè)首.
這個(gè)翻轉(zhuǎn)背后的操作其實(shí)就是 "把 endPointer 定位到 curPointer 處, 并把 curPointer 設(shè)為 0".
關(guān)于標(biāo)記, 在這里不涉及. 下一句說(shuō)到常與 compact 方法一起使用, 是可以想像的, 因?yàn)?compact 方法對(duì)數(shù)據(jù)進(jìn)
行了壓縮, 有效數(shù)據(jù)的真實(shí)長(zhǎng)度發(fā)生了變化, 肯定需要用 flip 重新定位結(jié)束標(biāo)記.
在填充, 壓縮等數(shù)據(jù)操作時(shí), curPointer 估計(jì)都是自動(dòng)更新了位置的, 總是指向最后一個(gè)有效數(shù)據(jù), 所以每次調(diào)
用 flip() 后, endPointer 就指向了有效數(shù)據(jù)的結(jié)尾, 而 curPointer 指向了 0 (緩沖起始處).
舉個(gè)圖例:
(c 和 e 分別代表 curPointer 和 endPointer 兩個(gè)指針)
* 先是一個(gè)空的 ByteBuffer (大小為 10 字節(jié))
c
e
* 然后填充 5 字節(jié)數(shù)據(jù)
0 1 2 3 4
e c
此時(shí), endPointer 尚在 0 處, curPointer 移到了數(shù)據(jù)結(jié)尾.
經(jīng)測(cè)試, 此時(shí)若取數(shù)據(jù), 將得到 5 個(gè)字節(jié), 內(nèi)容通常為 0 (也有可能是未知), 因?yàn)閷?shí)際上取到的是從 c 處到緩沖
區(qū)實(shí)際結(jié)束處的 5 個(gè)未初始化的字節(jié).
(QZone 字體處理不正確, 此處 c 是在 4 的下面, e 在 0 的下面)
* 調(diào)用一次 flip() 后
0 1 2 3 4
c e
此時(shí), endPointer 先被移到 curPointer, 然后 curPointer 移到 0.
通過(guò)測(cè)試可見(jiàn), ByteBuffer 取數(shù)據(jù)時(shí), 是從 curPointer 起, 到 endPointer 止, 若 curPointer > endPointer, 則取到緩沖區(qū)結(jié)束.
(QZone 字體處理不正確, 此處 c 是在 0 的下面, e 在 4 的下面)
再看上面代碼的關(guān)鍵片段, 行 8 處調(diào)用 flip() 即有兩個(gè)作用, 一是將 curPointer 移到 0, 二是將 endPointer 移到有效數(shù)據(jù)結(jié)尾.
此行可由以下兩行代替:
buff.limit( buff.position());
buff.position( 0 );
可見(jiàn)對(duì)其工作原理的理解, 應(yīng)該是正確的.
考試大編輯整理總結(jié)如下:
1. put 數(shù)據(jù)時(shí), 不會(huì)自動(dòng)清除緩沖區(qū)中現(xiàn)有的數(shù)據(jù).
2. 每一次 get 或 put 后, curPointer 都將向緩沖區(qū)尾部移動(dòng), 移動(dòng)量=操作的數(shù)據(jù)量.
3. get/put 均是從 curPointer 起, 到 curPointer + 操作的數(shù)據(jù)長(zhǎng)度止.
4. get/put 操作中, 若 curPointer 超過(guò)了 endPointer 或緩沖區(qū)總長(zhǎng)度, 將拋出 java.nio.BufferUnderflowException 異常.
注: curPointer 和 endPointer 只是為文中方便描述命名的, 實(shí)際分別對(duì)應(yīng)到 ByteBuffer.position() 和 ByteBuffer.limit() 兩個(gè)方法.
疑惑:
curPointer 是用 ByteBuffer.position() 取值, 用 ByteBuffer.position( int ) 賦值, 不知道 JDK 為什么要用多態(tài)來(lái)實(shí)現(xiàn)這兩個(gè)功能, 按我的想法, 設(shè)計(jì)成 getPosition(), setPosition() 要好看好記得多啊.
//
...
1 FileChannel fcin = new FileInputStream( "d:/t.txt" ).getChannel();
2 FileChannel fcout = new FileOutputStream( new File( "d:/out_j.txt" )).getChannel();
3 ByteBuffer buff = ByteBuffer.allocate( 1024 );
4 long t1 = System.currentTimeMillis();
5
6 while( fcin.read( buff ) != 1 )
7 {
8 buff.flip();
9 fcout.write( buff );
10 buff.clear();
11 }
12
13 long t2 = System.currentTimeMillis();
14 long size = fcin.size();
15 javax.swing.JOptionPane.showMessageDialog( null, size + " 字節(jié), 耗時(shí) " + ( t2 t1 + 1 ) + " ms." );
...
SDK 文檔里對(duì) ByteBuffer 的說(shuō)明為:
public abstract class ByteBuffer
extends Buffer
implements Comparable
這說(shuō)明 ByteBuffer 是繼承于 Buffer 的抽象類(lèi), 實(shí)現(xiàn)了兩個(gè)接口.
行3 通過(guò) allocate() 分配了一塊 1024 字節(jié)的緩沖區(qū), 并返回一個(gè) ByteBuffer 對(duì)象. (抽象類(lèi)不能直接 new)
行6 fcin.read() 將數(shù)據(jù)讀入到 buff. 此處的 read() 是 FileChannel 類(lèi)的一個(gè)虛函數(shù).
行8 buff.flip() 這個(gè)調(diào)用就是開(kāi)頭一直無(wú)法理解的部分.
SDK 文檔里的對(duì) flip() 的說(shuō)明是:
public final Buffer flip()
反轉(zhuǎn)此緩沖區(qū)。首先對(duì)當(dāng)前位置設(shè)置限制,然后將該位置設(shè)置為零。如果已定義了標(biāo)記,則丟棄該標(biāo)記。
當(dāng)將數(shù)據(jù)從一個(gè)地方傳輸?shù)搅硪粋€(gè)地方時(shí),經(jīng)常將此方法與 compact 方法一起使用。
我最終的理解是: 文檔翻譯得太差了, 把不應(yīng)該翻譯的內(nèi)容也譯成了中文, 所以反而不容易理解.
關(guān)鍵就在以下 2 處:
當(dāng)前位置: 這個(gè)可以直觀地理解為緩沖區(qū)中的當(dāng)前數(shù)據(jù)指針, 或是 SQL 中的游標(biāo), 記為 curPointer.
限制: 這個(gè)可以理解成實(shí)際操作的緩沖區(qū)段的結(jié)束標(biāo)記, 記為 endPointer.
反轉(zhuǎn): 這個(gè)完全是對(duì) flip 這個(gè)詞不負(fù)責(zé)的翻譯, 如果參照 DirectX 里的 flip() 而譯為翻轉(zhuǎn)/翻頁(yè), 那就好理解得多, 就像寫(xiě)信/看信, 寫(xiě)/看完一頁(yè)后, 翻到下一頁(yè), 眼睛/筆從頁(yè)底重新移回頁(yè)首.
這個(gè)翻轉(zhuǎn)背后的操作其實(shí)就是 "把 endPointer 定位到 curPointer 處, 并把 curPointer 設(shè)為 0".
關(guān)于標(biāo)記, 在這里不涉及. 下一句說(shuō)到常與 compact 方法一起使用, 是可以想像的, 因?yàn)?compact 方法對(duì)數(shù)據(jù)進(jìn)
行了壓縮, 有效數(shù)據(jù)的真實(shí)長(zhǎng)度發(fā)生了變化, 肯定需要用 flip 重新定位結(jié)束標(biāo)記.
在填充, 壓縮等數(shù)據(jù)操作時(shí), curPointer 估計(jì)都是自動(dòng)更新了位置的, 總是指向最后一個(gè)有效數(shù)據(jù), 所以每次調(diào)
用 flip() 后, endPointer 就指向了有效數(shù)據(jù)的結(jié)尾, 而 curPointer 指向了 0 (緩沖起始處).
舉個(gè)圖例:
(c 和 e 分別代表 curPointer 和 endPointer 兩個(gè)指針)
* 先是一個(gè)空的 ByteBuffer (大小為 10 字節(jié))
c
e
* 然后填充 5 字節(jié)數(shù)據(jù)
0 1 2 3 4
e c
此時(shí), endPointer 尚在 0 處, curPointer 移到了數(shù)據(jù)結(jié)尾.
經(jīng)測(cè)試, 此時(shí)若取數(shù)據(jù), 將得到 5 個(gè)字節(jié), 內(nèi)容通常為 0 (也有可能是未知), 因?yàn)閷?shí)際上取到的是從 c 處到緩沖
區(qū)實(shí)際結(jié)束處的 5 個(gè)未初始化的字節(jié).
(QZone 字體處理不正確, 此處 c 是在 4 的下面, e 在 0 的下面)
* 調(diào)用一次 flip() 后
0 1 2 3 4
c e
此時(shí), endPointer 先被移到 curPointer, 然后 curPointer 移到 0.
通過(guò)測(cè)試可見(jiàn), ByteBuffer 取數(shù)據(jù)時(shí), 是從 curPointer 起, 到 endPointer 止, 若 curPointer > endPointer, 則取到緩沖區(qū)結(jié)束.
(QZone 字體處理不正確, 此處 c 是在 0 的下面, e 在 4 的下面)
再看上面代碼的關(guān)鍵片段, 行 8 處調(diào)用 flip() 即有兩個(gè)作用, 一是將 curPointer 移到 0, 二是將 endPointer 移到有效數(shù)據(jù)結(jié)尾.
此行可由以下兩行代替:
buff.limit( buff.position());
buff.position( 0 );
可見(jiàn)對(duì)其工作原理的理解, 應(yīng)該是正確的.
考試大編輯整理總結(jié)如下:
1. put 數(shù)據(jù)時(shí), 不會(huì)自動(dòng)清除緩沖區(qū)中現(xiàn)有的數(shù)據(jù).
2. 每一次 get 或 put 后, curPointer 都將向緩沖區(qū)尾部移動(dòng), 移動(dòng)量=操作的數(shù)據(jù)量.
3. get/put 均是從 curPointer 起, 到 curPointer + 操作的數(shù)據(jù)長(zhǎng)度止.
4. get/put 操作中, 若 curPointer 超過(guò)了 endPointer 或緩沖區(qū)總長(zhǎng)度, 將拋出 java.nio.BufferUnderflowException 異常.
注: curPointer 和 endPointer 只是為文中方便描述命名的, 實(shí)際分別對(duì)應(yīng)到 ByteBuffer.position() 和 ByteBuffer.limit() 兩個(gè)方法.
疑惑:
curPointer 是用 ByteBuffer.position() 取值, 用 ByteBuffer.position( int ) 賦值, 不知道 JDK 為什么要用多態(tài)來(lái)實(shí)現(xiàn)這兩個(gè)功能, 按我的想法, 設(shè)計(jì)成 getPosition(), setPosition() 要好看好記得多啊.