Java NIO系列教程(二) Channel通道介紹及FileChannel詳解

Channel是一個通道刮吧,可以通過它讀取和寫入數(shù)據(jù)打瘪,它就像自來水管一樣,網(wǎng)絡(luò)數(shù)據(jù)通過Channel讀取和寫入奋姿。通道與流的不同之處在于通道是雙向的,流只是在一個方向上移動(一個流必須是InputStream或者OutputStream的子類)素标,而且通道可以用于讀称诗、寫或者同事用于讀寫。因為Channel是全雙工的头遭,所以它可以比流更好地映射底層操作系統(tǒng)的API寓免。特別是在UNIX網(wǎng)絡(luò)編程模型中,底層操作系統(tǒng)的通道都是全雙工的计维,同時支持讀寫操作袜香。

NIO中通過channel封裝了對數(shù)據(jù)源的操作,通過channel 我們可以操作數(shù)據(jù)源鲫惶,但又不必關(guān)心數(shù)據(jù)源的具體物理結(jié)構(gòu)蜈首。

這個數(shù)據(jù)源可能是多種的。比如欠母,可以是文件欢策,也可以是網(wǎng)絡(luò)socket。在大多數(shù)應(yīng)用中赏淌,channel與文件描述符或者socket是一一對應(yīng)的踩寇。Channel用于在字節(jié)緩沖區(qū)和位于通道另一側(cè)的實體(通常是一個文件或套接字)之間有效地傳輸數(shù)據(jù)。

channel接口源碼:

package java.nio.channels;publicinterface Channel;

{

? ? publicboolean isOpen();

? ? publicvoidclose()throws IOException;

}

與緩沖區(qū)不同六水,通道API主要由接口指定姑荷。不同的操作系統(tǒng)上通道實現(xiàn)(Channel Implementation)會有根本性的差異,所以通道API僅僅描述了可以做什么缩擂。因此很自然地鼠冕,通道實現(xiàn)經(jīng)常使用操作系統(tǒng)的本地代碼。通道接口允許您以一種受控且可移植的方式來訪問底層的I/O服務(wù)胯盯。


Channel是一個對象懈费,可以通過它讀取和寫入數(shù)據(jù)。拿 NIO 與原來的 I/O 做個比較博脑,通道就像是流憎乙。所有數(shù)據(jù)都通過Buffer對象來處理。您永遠不會將字節(jié)直接寫入通道中叉趣,相反泞边,您是將數(shù)據(jù)寫入包含一個或者多個字節(jié)的緩沖區(qū)。同樣疗杉,您不會直接從通道中讀取字節(jié)氮双,而是將數(shù)據(jù)從通道讀入緩沖區(qū),再從緩沖區(qū)獲取這個字節(jié)宴抚。


Java NIO的通道類似流,但又有些不同:

既可以從通道中讀取數(shù)據(jù)奠蹬,又可以寫數(shù)據(jù)到通道。但流的讀寫通常是單向的嗡午。

通道可以異步地讀寫囤躁。

通道中的數(shù)據(jù)總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入荔睹。

正如上面所說狸演,從通道讀取數(shù)據(jù)到緩沖區(qū),從緩沖區(qū)寫入數(shù)據(jù)到通道僻他。如下圖所示:


Channel的實現(xiàn)

這些是Java NIO中最重要的通道的實現(xiàn):

FileChannel:從文件中讀寫數(shù)據(jù)

DatagramChannel:通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)

SocketChannel:通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)

ServerSocketChannel:可以監(jiān)聽新進來的TCP連接宵距,像Web服務(wù)器那樣。對每一個新進來的連接都會創(chuàng)建一個SocketChannel中姜。

正如你所看到的,這些通道涵蓋了UDP 和 TCP 網(wǎng)絡(luò)IO跟伏,以及文件IO丢胚。

FileChannel

FileChannel類可以實現(xiàn)常用的read,write以及scatter/gather操作受扳,同時它也提供了很多專用于文件的新方法携龟。這些方法中的許多都是我們所熟悉的文件操作。

FileChannel類的JDK源碼:

package java.nio.channels;

? ? publicabstractclassFileChannelextendsAbstractChannelimplements ByteChannel, GatheringByteChannel, ScatteringByteChannel

? ? {

? ? ? ? // This is a partial API listing

? ? ? ? // All methods listed here can throw java.io.IOExceptionpublicabstractintread (ByteBuffer dst,long position);

? ? ? ? publicabstractintwrite (ByteBuffer src,long position);

? ? ? ? publicabstractlong size();

? ? ? ? publicabstractlong position();

? ? ? ? publicabstractvoidposition (long newPosition);

? ? ? ? publicabstractvoidtruncate (long size);

? ? ? ? publicabstractvoidforce (boolean metaData);

? ? ? ? publicfinal FileLock lock();

? ? ? ? publicabstractFileLock lock (longposition,longsize,boolean shared);

? ? ? ? publicfinal FileLock tryLock();

? ? ? ? publicabstractFileLock tryLock (longposition,longsize,boolean shared);

? ? ? ? publicabstractMappedByteBuffer map (MapMode mode,longposition,long size);

? ? ? ? publicstaticclass MapMode;

? ? ? ? publicstaticfinal MapMode READ_ONLY;

? ? ? ? publicstaticfinal MapMode READ_WRITE;

? ? ? ? publicstaticfinal MapMode PRIVATE;

? ? ? ? publicabstractlongtransferTo (longposition,long count, WritableByteChannel target);

? ? ? ? publicabstractlongtransferFrom (ReadableByteChannel src,longposition,long count);

? ? }

文件通道總是阻塞式的勘高,因此不能被置于非阻塞模式∠矿現(xiàn)代操作系統(tǒng)都有復(fù)雜的緩存和預(yù)取機制,使得本地磁盤I/O操作延遲很少华望。網(wǎng)絡(luò)文件系統(tǒng)一般而言延遲會多些蕊蝗,不過卻也因該優(yōu)化而受益。面向流的I/O的非阻塞范例對于面向文件的操作并無多大意義赖舟,這是由文件I/O本質(zhì)上的不同性質(zhì)造成的蓬戚。對于文件I/O,最強大之處在于異步I/O(asynchronous I/O)宾抓,它允許一個進程可以從操作系統(tǒng)請求一個或多個I/O操作而不必等待這些操作的完成子漩。發(fā)起請求的進程之后會收到它請求的I/O操作已完成的通知。

  FileChannel對象是線程安全(thread-safe)的石洗。多個進程可以在同一個實例上并發(fā)調(diào)用方法而不會引起任何問題幢泼,不過并非所有的操作都是多線程的(multithreaded)。影響通道位置或者影響文件大小的操作都是單線程的(single-threaded)讲衫。如果有一個線程已經(jīng)在執(zhí)行會影響通道位置或文件大小的操作缕棵,那么其他嘗試進行此類操作之一的線程必須等待。并發(fā)行為也會受到底層的操作系統(tǒng)或文件系統(tǒng)影響。

  每個FileChannel對象都同一個文件描述符(file descriptor)有一對一的關(guān)系挥吵,所以上面列出的API方法與在您最喜歡的POSIX(可移植操作系統(tǒng)接口)兼容的操作系統(tǒng)上的常用文件I/O系統(tǒng)調(diào)用緊密對應(yīng)也就不足為怪了重父。本質(zhì)上講,RandomAccessFile類提供的是同樣的抽象內(nèi)容忽匈。在通道出現(xiàn)之前房午,底層的文件操作都是通過RandomAccessFile類的方法來實現(xiàn)的。FileChannel模擬同樣的I/O服務(wù)丹允,因此它的API自然也是很相似的郭厌。

  三者之間的方法對比:

  

FILECHANNELRANDOMACCESSFILEPOSIX SYSTEM CALL

read( )read( )read( )

write( )write( )write( )

size( )length( )fstat( )

position( )getFilePointer( )lseek( )

position (long newPosition)seek( )lseek( )

truncate( )setLength( )ftruncate( )

force( )getFD().sync( )fsync( )


下面是一個使用FileChannel讀取數(shù)據(jù)到Buffer中的示例:

package com.dxz.springsession.nio.demo1;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;publicclass FileChannelTest {

? ? /**? ? * @param args

? ? * @throws IOException

? ? */publicstaticvoidmain(String[] args)throws IOException {

? ? ? ? RandomAccessFile aFile =newRandomAccessFile("d:\\soft\\nio-data.txt", "rw");

? ? ? ? FileChannel inChannel = aFile.getChannel();

? ? ? ? ByteBuffer buf = ByteBuffer.allocate(48);

? ? ? ? intbytesRead = inChannel.read(buf);

? ? ? ? while(bytesRead != -1) {

? ? ? ? ? ? System.out.println("Read " + bytesRead);

? ? ? ? ? ? buf.flip();

? ? ? ? ? ? while (buf.hasRemaining()) {

? ? ? ? ? ? ? ? System.out.print((char) buf.get());

? ? ? ? ? ? }

? ? ? ? ? ? buf.clear();

? ? ? ? ? ? bytesRead = inChannel.read(buf);

? ? ? ? }

? ? ? ? aFile.close();

? ? ? ? System.out.println("wan");

? ? }

}

文件內(nèi)容:

1234567qwertrewq

uytrewq

hgfdsa

nbvcxz

iop89

輸出結(jié)果:

Read 481234567qwertrewq

uytrewq

hgfdsa

nbvcxz

iop89wan

注意 buf.flip() 的調(diào)用,首先讀取數(shù)據(jù)到Buffer雕蔽,然后反轉(zhuǎn)Buffer,接著再從Buffer中讀取數(shù)據(jù)折柠。下一節(jié)會深入講解Buffer的更多細節(jié)。

1批狐、打開FileChannel

在使用FileChannel之前扇售,必須先打開它。但是嚣艇,我們無法直接打開一個FileChannel承冰,需要通過使用一個InputStream、OutputStream或RandomAccessFile來獲取一個FileChannel實例食零。下面是通過RandomAccessFile打開FileChannel的示例:

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

FileChannel inChannel = aFile.getChannel();

2困乒、從FileChannel讀取數(shù)據(jù)

調(diào)用多個read()方法之一從FileChannel中讀取數(shù)據(jù)。如:

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

首先贰谣,分配一個Buffer娜搂。從FileChannel中讀取的數(shù)據(jù)將被讀到Buffer中。

然后吱抚,調(diào)用FileChannel.read()方法百宇。該方法將數(shù)據(jù)從FileChannel讀取到Buffer中。read()方法返回的int值表示了有多少字節(jié)被讀到了Buffer中秘豹。如果返回-1恳谎,表示到了文件末尾。

3憋肖、向FileChannel寫數(shù)據(jù)

使用FileChannel.write()方法向FileChannel寫數(shù)據(jù)因痛,該方法的參數(shù)是一個Buffer。如:

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)用的岸更。因為無法保證write()方法一次能向FileChannel寫入多少字節(jié)鸵膏,因此需要重復(fù)調(diào)用write()方法,直到Buffer中已經(jīng)沒有尚未寫入通道的字節(jié)怎炊。

4谭企、關(guān)閉FileChannel

用完FileChannel后必須將其關(guān)閉廓译。如:

channel.close();

5、FileChannel的position方法

有時可能需要在FileChannel的某個特定位置進行數(shù)據(jù)的讀/寫操作债查》乔可以通過調(diào)用position()方法獲取FileChannel的當(dāng)前位置。

也可以通過調(diào)用position(long pos)方法設(shè)置FileChannel的當(dāng)前位置盹廷。

這里有兩個例子:

long pos = channel.position();

channel.position(pos +123);

如果將位置設(shè)置在文件結(jié)束符之后征绸,然后試圖從文件通道中讀取數(shù)據(jù),讀方法將返回-1 —— 文件結(jié)束標(biāo)志俄占。

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

6甚带、FileChannel的size方法

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

long fileSize = channel.size();

7、FileChannel的truncate方法

可以使用FileChannel.truncate()方法截取一個文件鹰贵。截取文件時晴氨,文件將中指定長度后面的部分將被刪除。如:

channel.truncate(1024);

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

8瑞筐、FileChannel的force方法

FileChannel.force()方法將通道里尚未寫入磁盤的數(shù)據(jù)強制寫到磁盤上凄鼻。出于性能方面的考慮腊瑟,操作系統(tǒng)會將數(shù)據(jù)緩存在內(nèi)存中,所以無法保證寫入到FileChannel里的數(shù)據(jù)一定會即時寫到磁盤上块蚌。要保證這一點闰非,需要調(diào)用force()方法。

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

下面的例子同時將文件數(shù)據(jù)和元數(shù)據(jù)強制寫到磁盤上:

channel.force(true);

示例:

package com.dxz.nio;

import java.io.FileInputStream;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

public class FileChannelRead {

? ? static public void main(String args[]) throws Exception {

? ? ? ? FileInputStream fin = new FileInputStream("e:\\logs\\test.txt");

? ? ? ? // 獲取通道

? ? ? ? FileChannel fc = fin.getChannel();

? ? ? ? // 創(chuàng)建緩沖區(qū)

? ? ? ? ByteBuffer buffer = ByteBuffer.allocate(1024);

? ? ? ? // 讀取數(shù)據(jù)到緩沖區(qū)

? ? ? ? fc.read(buffer);

? ? ? ? buffer.flip();

? ? ? ? while (buffer.remaining() > 0) {

? ? ? ? ? ? byte b = buffer.get();

? ? ? ? ? ? System.out.print(((char) b));

? ? ? ? }

? ? ? ? fin.close();

? ? }

}

寫入:

package com.dxz.nio;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;publicclass FileChannelWrite {

? ? staticprivatefinalbytemessage[] = { 83, 111, 109, 101, 32, 98, 121, 116, 101, 115, 46 };

? ? staticpublicvoidmain(String args[])throws Exception {

? ? ? ? FileOutputStream fout =newFileOutputStream("e:\\logs\\test2.txt");

? ? ? ? FileChannel fc = fout.getChannel();

? ? ? ? ByteBuffer buffer = ByteBuffer.allocate(1024);

? ? ? ? for(inti = 0; i < message.length; ++i) {

? ? ? ? ? ? buffer.put(message[i]);

? ? ? ? }

? ? ? ? buffer.flip();

? ? ? ? fc.write(buffer);

? ? ? ? fout.close();

? ? }

}

9、FileChannel的transferTo和transferFrom方法--通道之間的數(shù)據(jù)傳輸

如果兩個通道中有一個是FileChannel纱控,那你可以直接將數(shù)據(jù)從一個channel(譯者注:channel中文常譯作通道)傳輸?shù)搅硗庖粋€channel辆毡。

transferFrom()

FileChannel的transferFrom()方法可以將數(shù)據(jù)從源通道傳輸?shù)紽ileChannel中(譯者注:這個方法在JDK文檔中的解釋為將字節(jié)從給定的可讀取字節(jié)通道傳輸?shù)酱送ǖ赖奈募校O旅媸且粋€簡單的例子:

通過FileChannel完成文件間的拷貝:

package com.dxz.springsession.nio.demo1;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.channels.FileChannel;publicclass FileChannelTest2 {

? ? publicstaticvoidmain(String[] args)throws IOException {

? ? ? ? RandomAccessFile aFile =newRandomAccessFile("d:\\soft\\fromFile.txt", "rw");

? ? ? ? FileChannel fromChannel = aFile.getChannel();


? ? ? ? RandomAccessFile bFile =newRandomAccessFile("d:\\soft\\toFile.txt", "rw");

? ? ? ? FileChannel toChannel = bFile.getChannel();

? ? ? ? longposition = 0;

? ? ? ? longcount = fromChannel.size();

? ? ? ? toChannel.transferFrom(fromChannel, position, count);

? ? ? ? aFile.close();

? ? ? ? bFile.close();

? ? ? ? System.out.println("over!");

? ? }

}

方法的輸入?yún)?shù)position表示從position處開始向目標(biāo)文件寫入數(shù)據(jù)甜害,count表示最多傳輸?shù)淖止?jié)數(shù)舶掖。如果源通道的剩余空間小于 count 個字節(jié),則所傳輸?shù)淖止?jié)數(shù)要小于請求的字節(jié)數(shù)尔店。

此外要注意眨攘,在SoketChannel的實現(xiàn)中主慰,SocketChannel只會傳輸此刻準(zhǔn)備好的數(shù)據(jù)(可能不足count字節(jié))。因此鲫售,SocketChannel可能不會將請求的所有數(shù)據(jù)(count個字節(jié))全部傳輸?shù)紽ileChannel中共螺。

transferTo()

transferTo()方法將數(shù)據(jù)從FileChannel傳輸?shù)狡渌腸hannel中。下面是一個簡單的例子:

package com.dxz.springsession.nio.demo1;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.channels.FileChannel;publicclass FileChannelTest3 {

? ? publicstaticvoidmain(String[] args)throws IOException {

? ? ? ? RandomAccessFile aFile =newRandomAccessFile("d:\\soft\\fromFile.txt", "rw");

? ? ? ? FileChannel fromChannel = aFile.getChannel();


? ? ? ? RandomAccessFile bFile =newRandomAccessFile("d:\\soft\\toFile.txt", "rw");

? ? ? ? FileChannel toChannel = bFile.getChannel();

? ? ? ? longposition = 0;

? ? ? ? longcount = fromChannel.size();

? ? ? ? fromChannel.transferTo(position, count, toChannel);

? ? ? ? aFile.close();

? ? ? ? bFile.close();

? ? ? ? System.out.println("over!");

? ? }

}

是不是發(fā)現(xiàn)這個例子和前面那個例子特別相似情竹?除了調(diào)用方法的FileChannel對象不一樣外藐不,其他的都一樣。

上面所說的關(guān)于SocketChannel的問題在transferTo()方法中同樣存在鲤妥。SocketChannel會一直傳輸數(shù)據(jù)直到目標(biāo)buffer被填滿佳吞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棉安,隨后出現(xiàn)的幾起案子底扳,更是在濱河造成了極大的恐慌,老刑警劉巖贡耽,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衷模,死亡現(xiàn)場離奇詭異,居然都是意外死亡蒲赂,警方通過查閱死者的電腦和手機阱冶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滥嘴,“玉大人木蹬,你說我怎么就攤上這事∪糁澹” “怎么了镊叁?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長走触。 經(jīng)常有香客問我晦譬,道長,這世上最難降的妖魔是什么互广? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任敛腌,我火速辦了婚禮,結(jié)果婚禮上惫皱,老公的妹妹穿的比我還像新娘像樊。我一直安慰自己,他們只是感情好旅敷,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布生棍。 她就那樣靜靜地躺著,像睡著了一般扫皱。 火紅的嫁衣襯著肌膚如雪足绅。 梳的紋絲不亂的頭發(fā)上捷绑,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音氢妈,去河邊找鬼粹污。 笑死,一個胖子當(dāng)著我的面吹牛首量,可吹牛的內(nèi)容都是我干的壮吩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼加缘,長吁一口氣:“原來是場噩夢啊……” “哼鸭叙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拣宏,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤沈贝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后勋乾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宋下,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年辑莫,在試婚紗的時候發(fā)現(xiàn)自己被綠了学歧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡各吨,死狀恐怖枝笨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揭蜒,我是刑警寧澤横浑,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站忌锯,受9級特大地震影響伪嫁,放射性物質(zhì)發(fā)生泄漏领炫。R本人自食惡果不足惜偶垮,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帝洪。 院中可真熱鬧似舵,春花似錦、人聲如沸葱峡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砰奕。三九已至蛛芥,卻和暖如春提鸟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仅淑。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工称勋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涯竟。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓赡鲜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庐船。 傳聞我的和親對象是個殘疾皇子银酬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    zhisheng_blog閱讀 1,120評論 0 7
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API筐钟,可以替代標(biāo)準(zhǔn)的Java I...
    JackChen1024閱讀 7,555評論 1 143
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API揩瞪,可以替代標(biāo)準(zhǔn)的Java I...
    編碼前線閱讀 2,268評論 0 5
  • 英語 舞蹈 和瑜伽跑步 我最愛的。一天至少做其中一件 耶耶耶
    時間有腳閱讀 81評論 0 0
  • 從去年冬天開始篓冲,這今天第一次重新找到在球場上的感覺壮韭。 什么感覺呢,就仿佛球是你手的一部分纹因,你可以完全控制它喷屋,你的意...
    李砍柴閱讀 428評論 2 4