天天看點

FileChannel(API詳解)

1、兩種擷取通道的方法

FileChannel.open()的方式

FileChannel channell = FileChannel.open(Paths.get("a.txt","c.txt"), StandardOpenOption.CREATE,StandardOpenOption.WRITE);
FileChannel channel2 = FileChannel.open(new File("a.txt").toPath(), StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE,StandardOpenOption.READ);      

path擷取

Paths.get()
new File(“a.txt”).toPath()      

OpenOption接口的實作類通常由StandardOpenOption枚舉進行代替。

public enum StandardOpenOption implements OpenOption {
READ,
WRITE,
APPEND,//累加
TRUNCATE_EXISTING,//如果該檔案已存在并且為寫入通路而打開,則其長度将被截斷為0。如果隻為讀取通路打開檔案,則忽略此選項。
CREATE,//不能單獨使用,要與WRITE配套使用,單獨使用會報錯java.nio.file.NoSuchFileException,如果檔案已存在,重複建立不會報錯
CREATE_NEW,//不能單獨使用,要與WRITE配套使用,如果檔案已存在,則出現異常java.nio.file.FileAlreadyExistsException
DELETE_ON_CLOSE,
SPARSE,//稀疏檔案,空閑位置不占記憶體(不要使用CREATE來建立稀疏檔案)
SYNC,//要求對檔案内容或中繼資料的每次更新都同步寫入底層儲存設備。如果這樣做,程式運作的效率就降低了。
DSYNC;//要求對檔案内容的每次更新都同步寫入底層儲存設備。 
//枚舉常量SYNC與DSYNC的差別:SYNC更新内容與中繼資料,而DSYNC隻更新内容,與force(boolean)方法作用一樣。      

從io流中獲得通道getChannel()

FileChannel inchannel = new FileInputStream("a.txt").getChannel();
FileChannel outchannel = new FileOutputStream("b.txt").getChannel();
FileChannel inchannel1 = new RandomAccessFile("a.txt","r").getChannel();
FileChannel outchannel1 = new RandomAccessFile("a.txt","rw").getChannel();      

2、read

ByteBuffer buffer = ByteBuffer.allocate(10);
ByteBuffer buffer1 = ByteBuffer.allocate(10);
ByteBuffer[] buffers = {buffer,buffer1};
channel2.read(buffer);//将位元組序列從此通道的目前位置讀入給定的緩沖區的目前位置,方法同步,傳回值正數為讀取的位元組數,0為未讀取到資料,可能是緩沖區中沒有剩餘空間了,-1是到了流的末端
channel2.read(buffer,2);//position代表通道的位置
channel2.read(buffers);//将通道目前位置的位元組序列讀入多個ByteBuffer緩沖區的remaining剩餘空間中,方法同步
channel2.read(buffers,0,8);//offset代表數組的下表,length為向後的緩沖區個數      

3、write

ByteBuffer buffer2 = ByteBuffer.wrap(new byte[]{1,2,3,4});
ByteBuffer buffer3 = ByteBuffer.wrap(new byte[]{11,21,31,41});
ByteBuffer[] buffers1 = {buffer2,buffer3};
channell.write(buffer2);//将一個緩沖區中remaining位元組序列寫入通道的目前位置,write方法同步
channell.write(buffer2,2);//将一個緩沖區中remaining位元組序列寫入通道的指定位置,此方法不改變痛的位置,write方法同步
channell.write(buffers);//将多個緩沖區中的remaining剩餘位元組序列寫入通道的目前位置,方法同步
channell.write(buffers1,0,8);//指定緩沖區數組的offset下表開始,向後length個位元組緩沖區,将每個緩沖區的remaining剩餘位元組序列寫入此通道的目前位置      

4、擷取和設定通道的位置、大小

channell.position();
channell.position(2);

channell.size();//此通道關聯檔案的目前大小      

5、long transferTo(position,count,WritableByteChannel dest)

1)position:檔案中的位置,從此位置開始傳輸,必須為非負數。

2)count:要傳輸的最大位元組數;必須為非負數。

3)dest:目标通道。

long transferTo(position,count,WritableByteChannel dest)方法的作用是将位元組從此通道的檔案傳輸到給定的可寫入位元組通道。

1、試圖讀取從此通道的檔案中給定position處開始的count個位元組,并将其寫入目标通道的目前位置。

2、此方法的調用不一定傳輸所有請求的位元組,是否傳輸取決于通道的性質和狀态。

如果此通道的檔案從給定的position處開始所包含的位元組數小于count個位元組,或者如果目标通道是非阻塞的并且其輸出緩沖區中的自由空間少于count個位元組,則所傳輸的位元組數要小于請求的位元組數。

3、此方法不修改此通道的位置。如果給定的位置大于該檔案的目前大小,則不傳輸任何位元組,否則從目标通道的position位置起始開始寫入各位元組,然後将該位置增加寫入的位元組數。

4、與從此通道讀取并将内容寫入目标通道的簡單循環語句相比,此方法可能高效得多。很多作業系統可将位元組直接從檔案系統緩存傳輸到目标通道,而無須實際複制各位元組。

6、long transferFrom(ReadableByteChannel src,position,count)

1)src:源通道。

2)position:檔案中的位置,從此位置開始傳輸;必須為非負數。

3)count:要傳輸的最大位元組數;必須為非負數。

注意,參數position是指目前通道的位置,而不是指src源通道的位置。參數position針對于調用transferTo()或transferFrom()方法的對象。

long transferFrom(ReadableByteChannel src,position,count)方法的作用是将位元組從給定的可讀取位元組通道傳輸到此通道的檔案中。

1、試着從源通道中最多讀取count個位元組,并将其寫入到此通道的檔案中從給定position處開始的位置。

2、此方法的調用不一定傳輸所有請求的位元組;是否傳輸取決于通道的性質和狀态。

如果源通道的剩餘空間小于count個位元組,或者如果源通道是非阻塞的并且其輸入緩沖區中直接可用的空間小于count個位元組,則所傳輸的位元組數要小于請求的位元組數。

3、此方法不修改此通道的位置。如果給定的位置大于該檔案的目前大小,則不傳輸任何位元組。從源通道中的目前位置開始讀取各位元組寫入到目前通道,然後将src通道的位置增加讀取的位元組數。

4、與從源通道讀取并将内容寫入此通道的簡單循環語句相比,此方法可能高效得多。很多作業系統可将位元組直接從源通道傳輸到檔案系統緩存,而無須實際複制各位元組。

7、截斷緩沖區(在源檔案上截取,并不是得到新檔案)

channel2.truncate(100);      

truncate(long size)方法的作用是将此通道的檔案截取為給定大小。

如果給定大小小于該檔案的目前大小,則截取該檔案,丢棄檔案新末尾後面的所有位元組。

如果給定大小大于或等于該檔案的目前大小,則不修改檔案。

無論是哪種情況,如果此通道的檔案位置大于給定大小,則将位置設定為該大小。

8、将通道檔案區域直接映射到記憶體 map()

MappedByteBuffer map(FileChannel.MapMode mode,long position,long size)方法的作用是将此通道的檔案區域直接映射到記憶體中。

1)mode:根據隻讀、讀取/寫入或專用(寫入時複制)來映射檔案,分别為FileChannel.MapMode類中所定義的READ_ONLY、READ_WRITE和PRIVATE;

2)position:檔案中的位置,映射區域從此位置開始;必須為非負數。

3)size:要映射的區域大小;必須為非負數且不大于Integer.MAX_VALUE。

可以通過下列3種模式将檔案區域映射到記憶體中。

1)隻讀:試圖修改得到的緩沖區将導緻抛出ReadOnlyBufferException異常。(MapMode.READ_ONLY)

2)讀取/寫入:對得到的緩沖區的更改最終将傳播到檔案;該更改對映射到同一檔案的其他程式不一定是可見的。(MapMode.READ_WRITE)

3)專用:對得到的緩沖區的更改不會傳播到檔案,并且該更改對映射到同一檔案的其他程式也不是可見的;相反,會建立緩沖區已修改部分的專用副本。(MapMode.PRIVATE)

總結:

1、對于隻讀映射關系,此通道必須可以進行讀取操作;對于讀取/寫入或專用映射關系,此通道必須可以進行讀取和寫入操作。

2、此方法傳回的已映射位元組緩沖區位置為零,限制和容量為size;其标記是不确定的。在緩沖區本身被作為垃圾回收之前,該緩沖區及其表示的映射關系都是有效的。

3、映射關系一經建立,就不再依賴于建立它時所用的檔案通道。特别是關閉該通道對映射關系的有效性沒有任何影響。

4、對于大多數作業系統而言,與通過普通的read()和write()方法讀取或寫入數千位元組的資料相比,将檔案映射到記憶體中開銷更大。從性能的觀點來看,通常将相對較大的檔案映射到記憶體中才是值得的。

MappedByteBuffer的簡單介紹:

它是直接位元組緩沖區,其内容是檔案的記憶體映射區域。映射的位元組緩沖區是通過FileChannel.map()方法建立的。此類用特定于記憶體映射檔案區域的操作擴充ByteBuffer類。

public abstract class MappedByteBuffer extends ByteBuffer      

作為ByteBuffer的子類,除了具有父類的方法外,還新增了

force()将此緩沖區所做的内容更改強制寫入包含映射檔案的儲存設備中。

load()将此緩沖區内容加載到實體記憶體中。

isLoaded()判斷次緩沖區的内容是否位于實體記憶體中。

FileChannel類或MappedByteBuffer類對檔案進行操作時,在大部分情況下,它們的效率并不比使用InputStream或OutputStream高很多,這是因為NIO的出現是為了解決操作I/O線程阻塞的問題,使用NIO就把線程變成了非阻塞,這樣就提高了運作效率。

NIO真正的優勢:非阻塞。

繼續閱讀