繼續上面的Buffer講解。
4.緩衝區分片
在NIO中,除了可以分配或者包裝一個緩衝區對象外,還可以根據現有的緩衝區對象來創建一個子緩衝區,即在現有緩衝區上切
出一片來作爲一個新的緩衝區,但現有的緩衝區與創建的子緩衝區在底層數組層面上是數據共享的,也就是說,子緩衝區相當於是
現有緩衝區的一個視圖窗口。調用slice()方法可以創建一個子緩衝區,讓我們通過例子來看一下:
/** * 緩衝區分片 */ public class BufferSlice { static public void main( String args[] ) throws Exception { ByteBuffer buffer = ByteBuffer.allocate( 10 ); // 緩衝區中的數據0-9 for (int i=0; i<buffer.capacity(); ++i) { buffer.put( (byte)i ); } // 創建子緩衝區 buffer.position( 3 ); buffer.limit( 7 ); ByteBuffer slice = buffer.slice(); // 改變子緩衝區的內容 for (int i=0; i<slice.capacity(); ++i) { byte b = slice.get( i ); b *= 10; slice.put( i, b ); } buffer.position( 0 ); buffer.limit( buffer.capacity() ); while (buffer.remaining()>0) { System.out.println( buffer.get() ); } } }
在該示例中,分配了一個容量大小爲10 的緩衝區,並在其中放入了數據0-9,而在該緩衝區基礎之上又創建了一個子緩衝區,並
改變子緩衝區中的內容,從最後輸出的結果來看,只有子緩衝區“可見的”那部分數據發生了變化,並且說明子緩衝區與原緩衝區是
數據共享的,輸出結果如下所示:
0
1
2
30
40
50
60
7
8
9
5.只讀緩衝區
看名字就知道,這個緩衝區只可以讀,不能修改。。可以通過調用緩衝區的 asReadOnlyBuffer()方法,將任何常規緩
衝區轉 換爲只讀緩衝區,這個方法返回一個與原緩衝區完全相同的緩衝區,並與原緩衝區共享數據,只不過它是隻讀的。如果原
緩衝區的內容發生了變化,只讀緩衝區的內容也隨之發生變化:
/** * 只讀緩衝區 */ public class ReadOnlyBuffer { static public void main( String args[] ) throws Exception { ByteBuffer buffer = ByteBuffer.allocate( 10 ); // 緩衝區中的數據0-9 for (int i=0; i<buffer.capacity(); ++i) { buffer.put( (byte)i ); } // 創建只讀緩衝區 ByteBuffer readonly = buffer.asReadOnlyBuffer(); // 改變原緩衝區的內容 for (int i=0; i<buffer.capacity(); ++i) { byte b = buffer.get( i ); b *= 10; buffer.put( i, b ); } readonly.position(0); readonly.limit(buffer.capacity()); // 只讀緩衝區的內容也隨之改變 while (readonly.remaining()>0) { System.out.println( readonly.get()); } } }
6.直接緩衝區
直接緩衝區是爲加快I/O 速度,使用一種特殊方式爲其分配內存的緩衝區,JDK文檔中的描述爲:給定一個直接字節緩衝區,Java虛擬機將盡最大努力直接對它執行本機I/O操作。也就是說,它會在每一次調用底層操作系統的本機I/O 操作之前(或之後),嘗試避免將緩衝區的內容拷貝到一箇中間緩衝區中或者從一箇中間緩衝區中拷貝數據。要分配直接緩衝區,需要調用allocateDirect()方法,而不是allocate()方法,使用方式與普通緩衝區並無區別,如下面的拷貝文件示例:
/** * 直接緩衝區 * Zero Copy 減少了一個拷貝的過程 */ public class DirectBuffer { static public void main( String args[] ) throws Exception { //在Java裏面存的只是緩衝區的引用地址 //管理效率 //首先我們從磁盤上讀取剛纔我們寫出的文件內容 String infile = "D://test.txt"; FileInputStream fin = new FileInputStream( infile ); FileChannel fcin = fin.getChannel(); //把剛剛讀取的內容寫入到一個新的文件中 String outfile = String.format("D://testcopy.txt"); FileOutputStream fout = new FileOutputStream(outfile); FileChannel fcout = fout.getChannel(); // 使用allocateDirect,而不是allocate ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { buffer.clear(); int r = fcin.read(buffer); if (r==-1) { break; } buffer.flip(); fcout.write(buffer); } } }
7.內存映射
內存映射是一種讀和寫文件數據的方法,它可以比常規的基於流或者基於通道的I/O 快的多。內存映射文件I/O 是通過使文件中的
數據出現爲 內存數組的內容來完成的,這其初聽起來似乎不過就是將整個文件讀到內存中,但是事實上並不是這樣。一般來說,
只有文件中實際讀取或者寫入的部分纔會映射到內存中。如下面的示例代碼:
/** * IO映射緩衝區 */ public class MappedBuffer { static private final int start = 0; static private final int size = 26; static public void main( String args[] ) throws Exception { RandomAccessFile raf = new RandomAccessFile( "E://test.txt", "rw" ); FileChannel fc = raf.getChannel(); //把緩衝區跟文件系統進行一個映射關聯 //只要操作緩衝區裏面的內容,文件內容也會跟着改變 MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,start, size ); mbb.put( 0, (byte)97 ); //a mbb.put( 25, (byte)122 ); //z raf.close(); } }