能夠想象一個煤礦,通道時一個包含煤層(數據)的礦藏,而緩衝區則是派送到礦藏的卡車。卡車載滿煤炭而歸,而咱們再從卡車上得到煤炭。也就就說,咱們並無與通道直接交互,咱們只是和緩衝區交互,並把緩衝區派送到通道。通道要麼從緩衝區得到數據,要麼像緩衝區發送數據。java
惟一直接與通道交互的緩衝區是ByteBuffer,從名字上就能夠看出,這是一個能夠存儲字節的存儲器。下面是ByteBuffer的部分JDK源碼(重要的方法)數組
public abstract class ByteBuffer extends Bufferimplements Comparable<ByteBuffer> /** * Allocates a new byte buffer. * * <p> The new buffer's position will be zero, its limit will be its * capacity, and its mark will be undefined. It will have a {@link #array * </code>backing array<code>}, and its {@link #arrayOffset </code>array * offset<code>} will be zero. * * @param capacity * The new buffer's capacity, in bytes * * @return The new byte buffer * * @throws IllegalArgumentException * If the <tt>capacity</tt> is a negative integer */ public static ByteBuffer allocate(int capacity) { if (capacity < 0) throw new IllegalArgumentException(); return new HeapByteBuffer(capacity, capacity); } /** * 靜態方法,獲得原始字節形式的緩衝區。。 */ public static ByteBuffer wrap(byte[] array) { return wrap(array, 0, array.length); } /** * 下面這些方法用於獲得其餘基本類型的緩衝區 */ public abstract ShortBuffer asShortBuffer(); public abstract CharBuffer asCharBuffer(); public abstract IntBuffer asIntBuffer(); public abstract long getLong(); public abstract DoubleBuffer asDoubleBuffer(); public abstract FloatBuffer asFloatBuffer();
從上面的代碼片斷中,咱們能夠看到,這個類其實一個很基礎的類:經過告知分配多少存儲空間來建立一個BuyeBuffer對象,而且有一些方法,用於產生成其餘基本類型的緩衝區,從而以字節或者其餘基本類型方式讀取數據或者輸出數據,可是,要注意的是,沒有辦法輸出或者讀取對象(即便是String字符串對象也不行,可是能夠經過String.getBytes()轉換爲字節類型處理),這樣的處理方式雖然比較低級,可是,這正是大多數操做系統中更有效的映射方式。app
舊有的I/O類庫中有三個類被修改,用以產生FileChannel。這三個被修改的類是FileInputStream,FileOutStream,RandomAccessFile。dom
下面是一個簡單的文件複製程序post
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class ChannelCopy { public static final int BSIZE = 1024; /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { String arg1 = "E:/test/in.txt"; String arg2 = "E:/test/out.txt"; FileChannel inChannel = new FileInputStream(new File(arg1)).getChannel(); FileChannel outChannel = new FileOutputStream(new File(arg2),false).getChannel();//false:覆蓋寫,true:追加寫 ByteBuffer buffer = ByteBuffer.allocate(BSIZE); while(inChannel.read(buffer)!=-1) { buffer.flip();//必定要加上 ,read操做後須要次方法 outChannel.write(buffer); buffer.clear();/必定要加上,若是read一次後,還須要對緩衝區進一步read,那麼也必需要加上次方法 } System.out.println("Sucess!"); } }
這個程序很簡單,就不要作過多的介紹,可是須要注意的是,是最後幾行代碼。spa
1)一旦調用read來告知FileChannel向ByteBuffer存儲字節,那就必須調用緩衝區的flip方法,讓別人作好讀取字節的準備,在上述程序中,若是沒有flip()方法,那麼就沒法把內容寫到目標文件中(其實寫了,可是內容是空白)。爲何會出現這種狀況呢,下面是加上flip()方法和沒有加上flip()方法ByteBuffer對象的狀態。操作系統
java.nio.HeapByteBuffer[pos=0 lim=21 cap=1024] (加上flip()方法)指針
java.nio.HeapByteBuffer[pos=21 lim=1024 cap=1024](沒有加上flip()方法)code
咱們能夠很清楚的看到,若是不加上flip方法,那麼讀取的內容實際上是緩衝區當前位置日後的內容(以後的內容固然是空白了,緩衝區還沒數據寫到那呢。),而真正的內容應該是當前位置以前的內容。 我的以爲read操做後,必須就跟上flip()方法。orm
2)write()操做後,信息仍然在緩衝區中,接着clear()操做會從新設置緩衝區內部的指針,以便緩衝區在另外一個read()操做期間可以作好接受數據的準備。換句話說,若是咱們打算使用緩衝區執行進一步的read()操做,咱們也必須得調用clear()方法來爲每一個read()作好準備。爲何?我代碼沒有跟下去,若是沒有clear()方法,inChnanel.read(buffer)會一直等於0,致使了死循環,有高人解釋下爲何?
ByteBuffer緩衝區的細節
1)ByteBuffer是惟一能將數據寫入或讀出的方式,咱們只能使用經過建立一個獨立的基本類型緩衝器,或者使用「as」方法從ByteBuffer中得到。也就說,咱們不能
把基本類型的緩衝器轉換成ByteBuffer(這個其實看源碼就知道了,他們直接表示繼承關係,不能強制轉化)
2)Buffer由數據和能夠高效地訪問及操縱這些數據的四個索引組成,這四個索引分別是:mark(標記,其實我沒明白這個的做用),position(位置),limit(界限),capacity(容量)。下面是用於設置和復位索引以及查詢它們值的方法。
capacity()
返回緩衝區容量
clear()
清空緩衝區,將position置爲0,limit設置爲容量大小(其實數據並無清楚,只是次方法後,每次重頭開始寫入數據,覆蓋了原有數據)
flip()
將limit置爲position,position置爲0.
limit()
返回limit值
limit(int lim)
設置limit值
mark()
將mark設置爲position
position()
返回position的值
position(int pos)
設置position的值
remaining()
返回(limit-position)
hasRemaining()
return position < limit;
reset()
把postion設置爲mark
內存映射文件
內存映射文件容許咱們建立和修改那些由於太大而不能放入內存的文件,有了內存映射文件,咱們就能夠假定整個文件都在內存中,並且徹底能夠把他們當作很是大的數組來訪問。
咱們能夠看到ByteBuffer是一個abstract class.其有兩個子類,一個是HeapByteBuffer,咱們以前將的nio的操做,就是經過其來實現的。另外一個就是MappedByteBuffer這個就是咱們內存映射文件須要用到的。咱們看到在FileChannel有一個map方法,以下所示,該方法就能夠獲得相應的MappedByteBuffer
* @see java.nio.channels.FileChannel.MapMode * @see java.nio.MappedByteBuffer */ public abstract MappedByteBuffer map(MapMode mode, long position, long size) throws IOException; //mode = FileChannel.MapMode.READ_WRITE等等,position = 映射文件的起始位置,size = 映射區域大小
注意:「映射讀"能夠經過FileInpuStream.getChannel().map()來處理,可是」映射寫(讀寫)「 必須得用RandomAccesssFile().getChannel().map(),而不能經過FileOutputStream獲得緩衝器。