Java NIO 簡(jiǎn)單教程(三)

接上一篇JAVA NIO簡(jiǎn)單教程(二)

7.Java NIO FileChannel

Java NIO FileChannel 是連接到文件的通道,使用文件通道,您可以從文件中讀取數(shù)據(jù),并將數(shù)據(jù)寫(xiě)入文件。 Java NIO FileChannel 類是 NIO 使用標(biāo)準(zhǔn) Java IO API 讀取文件的替代方法。FileChannel 不能設(shè)置為非阻塞模式,它始終以阻塞模式運(yùn)行。

打開(kāi)文件通道

在您可以使用 FileChannel 之前,您必須打開(kāi)它, 您不能直接使用 FileChannel。 您需要通過(guò) InputStream、OutputStream 或 RandomAccessFile 獲取 FileChannel。 以下是通過(guò) RandomAccessFile 打開(kāi) FileChannel 的方法:

RandomAccessFile aFile     = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel      inChannel = aFile.getChannel();

從 FileChannel 讀取數(shù)據(jù)

要從 FileChannel 讀取數(shù)據(jù),您可以調(diào)用 read() 方法之一。下面是一個(gè)例子:

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);

首先分配一個(gè)緩沖區(qū)。從 FileChannel 讀取的數(shù)據(jù)被讀入到 Buffer 中。其次,調(diào)用 FileChannel.read() 方法。該方法從 FileChannel 讀取數(shù)據(jù)到 Buffer 中。 read() 方法返回的 int 表示有多少字節(jié)被寫(xiě)入緩沖區(qū),如果返回 -1,則到達(dá)文件末尾。

將數(shù)據(jù)寫(xiě)入 FileChannel

將數(shù)據(jù)寫(xiě)入 FileChannel 是使用 FileChannel.write() 方法完成的,該方法將 Buffer 作為參數(shù)。下面是一個(gè)例子:

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    channel.write(buf);
}

注意 FileChannel.write() 方法是如何在 while 循環(huán)中調(diào)用的。無(wú)法保證 write() 方法寫(xiě)入 FileChannel 的字節(jié)數(shù)。因此,我們重復(fù) write() 調(diào)用,直到 Buffer 沒(méi)有更多字節(jié)要寫(xiě)入。

FileChannel Position

讀取或?qū)懭?FileChannel 時(shí),您在特定位置執(zhí)行此操作。您可以通過(guò)調(diào)用 position() 方法獲取 FileChannel 對(duì)象的當(dāng)前位置。您還可以通過(guò)調(diào)用 position(long pos) 方法來(lái)設(shè)置 FileChannel 的位置。

這里有兩個(gè)例子:

long pos channel.position();
channel.position(pos +123);

如果您在文件結(jié)束后設(shè)置position,并嘗試從channel讀取,您將獲得 -1 ( 文件結(jié)束標(biāo)記)。

如果將位置設(shè)置在文件結(jié)束符之后,然后向通道中寫(xiě)數(shù)據(jù),文件將撐大到當(dāng)前位置并寫(xiě)入數(shù)據(jù)。這可能導(dǎo)致“文件空洞”,磁盤(pán)上物理文件中寫(xiě)入的數(shù)據(jù)間有空隙。

FileChannel的size方法

FileChannel實(shí)例的size()方法將返回該實(shí)例所關(guān)聯(lián)文件的大小。如:

long fileSize = channel.size();

FileChannel的truncate方法

可以使用FileChannel.truncate()方法截取一個(gè)文件。截取文件時(shí),你就是在給定的長(zhǎng)度處將它截?cái)?。如?/p>

channel.truncate(1024);

這個(gè)例子截取文件的前1024個(gè)字節(jié)。

FileChannel的force方法

FileChannel.force()方法將通道里尚未寫(xiě)入磁盤(pán)的數(shù)據(jù)強(qiáng)制寫(xiě)到磁盤(pán)上。出于性能方面的考慮,操作系統(tǒng)會(huì)將數(shù)據(jù)緩存在內(nèi)存中,所以無(wú)法保證寫(xiě)入到FileChannel里的數(shù)據(jù)一定會(huì)即時(shí)寫(xiě)到磁盤(pán)上。要保證這一點(diǎn),需要調(diào)用force()方法。

force()方法有一個(gè)boolean類型的參數(shù),指明是否同時(shí)將文件元數(shù)據(jù)(權(quán)限信息等)寫(xiě)到磁盤(pán)上。

下面的例子同時(shí)將文件數(shù)據(jù)和元數(shù)據(jù)強(qiáng)制寫(xiě)到磁盤(pán)上:

channel.force(true);

8.Java NIO SocketChannel

Java NIO中的SocketChannel是一個(gè)連接到TCP網(wǎng)絡(luò)套接字的通道??梢酝ㄟ^(guò)以下2種方式創(chuàng)建SocketChannel:

  • 1.打開(kāi)一個(gè)SocketChannel并連接到互聯(lián)網(wǎng)上的某臺(tái)服務(wù)器。
  • 2.一個(gè)新連接到達(dá)ServerSocketChannel時(shí),會(huì)創(chuàng)建一個(gè)SocketChannel。

打開(kāi) SocketChannel

下面是SocketChannel的打開(kāi)方式:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.hhdatabase.com.cn", 80));

關(guān)閉 SocketChannel

當(dāng)用完SocketChannel之后調(diào)用SocketChannel.close()關(guān)閉SocketChannel:

socketChannel.close();

從 SocketChannel 讀取數(shù)據(jù)

要從SocketChannel中讀取數(shù)據(jù),調(diào)用一個(gè)read()的方法之一。以下是例子:

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);

首先,分配一個(gè)Buffer。從SocketChannel讀取到的數(shù)據(jù)將會(huì)放到這個(gè)Buffer中。

然后,調(diào)用SocketChannel.read()。該方法將數(shù)據(jù)從SocketChannel 讀到Buffer中。read()方法返回的int值表示讀了多少字節(jié)進(jìn)Buffer里。如果返回的是-1,表示已經(jīng)讀到了流的末尾(連接關(guān)閉了)。

寫(xiě)入 SocketChannel

寫(xiě)數(shù)據(jù)到SocketChannel用的是SocketChannel.write()方法,該方法以一個(gè)Buffer作為參數(shù)。示例如下:

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    channel.write(buf);
}

注意SocketChannel.write()方法的調(diào)用是在一個(gè)while循環(huán)中的。Write()方法無(wú)法保證能寫(xiě)多少字節(jié)到SocketChannel。所以,我們重復(fù)調(diào)用write()直到Buffer沒(méi)有要寫(xiě)的字節(jié)為止。

非阻塞模式

可以設(shè)置 SocketChannel 為非阻塞模式(non-blocking mode),設(shè)置之后,就可以在異步模式下調(diào)用connect()、 read() 和write()了。

connect()

如果SocketChannel在非阻塞模式下,此時(shí)調(diào)用connect(),該方法可能在連接建立之前就返回了。為了確定連接是否建立,可以調(diào)用finishConnect()的方法。像這樣:

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));

while(! socketChannel.finishConnect() ){
    //wait, or do something else...
}

write()

非阻塞模式下,write()方法在尚未寫(xiě)出任何內(nèi)容時(shí)可能就返回了。所以需要在循環(huán)中調(diào)用write()。前面已經(jīng)有例子了,這里就不贅述了。

read()

非阻塞模式下,read()方法在尚未讀取到任何數(shù)據(jù)時(shí)可能就返回了。所以需要關(guān)注它的int返回值,它會(huì)告訴你讀取了多少字節(jié)。

非阻塞模式與選擇器

非阻塞模式與選擇器搭配會(huì)工作的更好,通過(guò)將一或多個(gè)SocketChannel注冊(cè)到Selector,可以詢問(wèn)選擇器哪個(gè)通道已經(jīng)準(zhǔn)備好了讀取,寫(xiě)入等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容