Java NIO操作(一)

基本概念描述

1.1 NIO

NIO即New IO,在KJDK1.4中引入复唤,IO和NIO具有相同的作用和目的站欺,但I(xiàn)O用到的是流,而NIO用到的是塊夸溶,所以NIO的效率要比IO高。
在JAVA API中提供了兩套NIO凶硅,分別是標(biāo)準(zhǔn)輸入輸出NIO缝裁,以及網(wǎng)絡(luò)編程N(yùn)IO。
Java NIO是同步非阻塞通信足绅。舉個(gè)例子就是叫一個(gè)線程不停地觀察IO操作所處的狀態(tài)捷绑,根據(jù)狀態(tài)去處理。同步的原因是它的read/write/accept方法的內(nèi)核IO操作都會(huì)阻塞當(dāng)前線程氢妈。

1.2 流和塊的比較

IO是以流的方式處理數(shù)據(jù)粹污,而NIO是以塊的方式處理數(shù)據(jù)。

面向流的IO一次一個(gè)字節(jié)的處理數(shù)據(jù)首量,一個(gè)輸入流產(chǎn)生一個(gè)字節(jié)壮吩,一個(gè)輸出流消費(fèi)一個(gè)字節(jié)进苍,有利于使用過濾器,但是面向流的IO通常處理得很慢鸭叙。

面向塊的IO以塊的方式處理數(shù)據(jù)觉啊,每一個(gè)操作都在一步中產(chǎn)生或者消費(fèi)一個(gè)數(shù)據(jù)塊。所以面向塊的IO通常處理得很快沈贝。

NIO基礎(chǔ)

Buffer和Channel是標(biāo)準(zhǔn)NIO中的核心對(duì)象杠人,Selector是網(wǎng)絡(luò)NIO的核心對(duì)象。
Channel是對(duì)原IO中流的模擬宋下,任何來源和目的數(shù)據(jù)都必須通過一個(gè)Channel對(duì)象嗡善,一個(gè)Buffer實(shí)質(zhì)上是一個(gè)容器對(duì)象,發(fā)給Channel的所有對(duì)象都必須先放到Buffer中学歧;同樣的罩引,從Channel中讀取的任何數(shù)據(jù)都要讀到Buffer中。

關(guān)于Buffer

Buffer是一個(gè)對(duì)象撩满,它包含一些要寫入的或讀出的數(shù)據(jù)蜒程,在NIO中,數(shù)據(jù)是放入Buffer對(duì)象的伺帘,而在IO中昭躺,數(shù)據(jù)是直接寫入或者讀到Stream對(duì)象的,應(yīng)用程序不能直接對(duì)Channel對(duì)象進(jìn)行讀寫操作伪嫁,而必須通過Buffer來進(jìn)行领炫,即Channel是通過Buffer來讀寫數(shù)據(jù)的。
在NIO中张咳,所有的數(shù)據(jù)都是用Buffer處理的帝洪,他是NIO讀寫數(shù)據(jù)的中轉(zhuǎn)池。Buffer實(shí)質(zhì)上是一個(gè)數(shù)組脚猾,通常是一個(gè)字節(jié)數(shù)據(jù)葱峡,但也可以是其他類型的數(shù)組。
使用Buffer讀寫數(shù)據(jù)一般遵循四個(gè)步驟:

  1. 寫入數(shù)據(jù)到Buffer
  2. 調(diào)用flip()方法
  3. 從Buffer中讀取數(shù)據(jù)
  4. 調(diào)用clear()方法或者compact()方法
    當(dāng)向Buffer寫入數(shù)據(jù)時(shí)龙助,Buffer會(huì)記錄寫了多少數(shù)據(jù)砰奕。一旦要讀取數(shù)據(jù),需要通過flip()方法將Buffer從寫模式切換到讀模式提鸟。在讀模式下军援,可以讀取之前寫入到Buffer中的所有數(shù)據(jù)。

讀完了數(shù)據(jù)之后称勋,可以采用clear()或者compact()方法清空整個(gè)緩沖區(qū)胸哥。clear()會(huì)清空整個(gè)緩沖區(qū)。compact()方法只會(huì)清除已經(jīng)讀過的數(shù)據(jù)赡鲜,任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處空厌,新寫入的數(shù)據(jù)將被放到緩沖區(qū)未讀數(shù)據(jù)的后面庐船。

關(guān)于Channel

Channel是一個(gè)對(duì)象,可以通過它讀取和寫入數(shù)據(jù)嘲更,可以看作IO中的流醉鳖。

  1. Channel是雙向的,既可以讀又可以寫哮内,而流是單向的
  2. Channel可以進(jìn)行異步的讀寫
  3. 對(duì)Channel的讀寫必須通過buffer對(duì)象
    在Java NIO中,Channel主要有以下幾種類型:
  • FileChannel: 從文件中讀取數(shù)據(jù)的
  • DatagramChannel:讀取UDP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)
  • SocketChannel:讀寫TCP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)
  • ServerSocketChannel:可以監(jiān)聽TCP連接

NIO的讀寫

從文件中讀取

在NIO系統(tǒng)中壮韭,任何時(shí)候執(zhí)行一個(gè)操作北发,都是從Channel讀取數(shù)據(jù)到Buffer
從文件讀取數(shù)據(jù)的步驟:

  1. 從FileInputStream獲取Channel
  2. 創(chuàng)建Buffer
  3. 從Channel讀取數(shù)據(jù)到Buffer
    具體的實(shí)現(xiàn)過程
    第一步: 獲取通道
FileInputStream fin = new FileInputStream("...");
FileChannel fc = fin.getChannel();

第二步:創(chuàng)建緩沖區(qū)

ByteBuffer buffer = ByteBuffer.allocate(1024);

第三步: 將數(shù)據(jù)從通道寫入緩沖區(qū)

fc.read(buffer);

寫入數(shù)據(jù)到文件

第一步: 獲取一個(gè)通道

FileOutputStream fout = new FileOutputStream("...");
FileChannel fc = fout.getChannel();

第二步:創(chuàng)建緩沖區(qū),將數(shù)據(jù)放入緩沖區(qū)

ByteBuffer buffer = ByteBuffer.allocate(1024);
for(int i = 0; i < message.length; i++){
   buffer.put(message[i]);
}
buffer.flip(); 

第三步:把緩沖區(qū)數(shù)據(jù)寫入通道中

fc.write(buffer);

注意點(diǎn): InputStream輸入流喷屋,用于讀入數(shù)據(jù)
OutputStream輸出流琳拨,用于寫出數(shù)據(jù)。

讀寫結(jié)合

  public static void copyFileWithNIO(String src, String dst) throws IOException {
        FileInputStream fin = new FileInputStream(new File(src));
        FileOutputStream fout = new FileOutputStream(new File(dst));

        FileChannel finChannel = fin.getChannel();
        FileChannel foutChannel = fout.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while (true) {
            int eof = finChannel.read(buffer);
            if (eof == -1) {
                break;
            }
//            重新設(shè)置一下buffer的position=0屯曹, limit=position
            buffer.flip();
//            開始寫
            foutChannel.write(buffer);
//            寫完要重置buffer,重新設(shè)置position=0, limit=capacity
            buffer.clear();
        }
        finChannel.close();
        foutChannel.close();
        fin.close();
        fout.close();
    }

需要注意的點(diǎn)

4.1檢查狀態(tài)

當(dāng)沒有更多的數(shù)據(jù)時(shí)狱庇,拷貝就算完成,此時(shí)恶耽,read()方法會(huì)返回-1,用該方法判斷是否讀完密任。

int r = fin.read(buffer);
if(r == -1) {
  System.out.println("文件讀取結(jié)束");
  break;
}

4.2 Buffer類的flip偷俭、clear方法

控制buffer狀態(tài)的三個(gè)變量

  • position:跟蹤已經(jīng)寫了多少數(shù)據(jù)或者讀了多少數(shù)據(jù)浪讳,它指向的是下一個(gè)字節(jié)來自哪個(gè)位置
  • limit:代表還有多少數(shù)據(jù)可以取出,或者還有多少空間可以寫入涌萤,它的值小于等于capacity
  • capacity:代表緩沖區(qū)的最大容量淹遵,一般新建一個(gè)緩沖區(qū)的時(shí)候,limit的值和capacity的值是相等的
    flip()负溪、clear()用于設(shè)置這些值
    flip()方法的源碼
public final Buffer flip() {
  limit = position;
  position = 0;
  mark = -1;
  return this;
}

flip()方法把當(dāng)前的指針位置position設(shè)置成了limit透揣,再將當(dāng)前指針position指向數(shù)據(jù)的最開始端,然后就可以將數(shù)據(jù)從緩沖區(qū)寫入通道川抡。position被設(shè)置為0,意味著我們得到的下一個(gè)字節(jié)是第一個(gè)字節(jié)辐真。limit已被設(shè)置為原來的position,這意味著它包括以前讀到的所有字節(jié)猖腕,并且一個(gè)字節(jié)也不少拆祈。

clear()方法的源碼

public final Buffer clear() {
  position = 0; 
  limit = capacity;
  mark = -1;
  return this;
}

clear()方法重新設(shè)置了緩沖區(qū)以便接收更多的字節(jié)。

原文出處:http://blog.csdn.net/suifeng3051/article/details/48160753

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末倘感,一起剝皮案震驚了整個(gè)濱河市放坏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌老玛,老刑警劉巖淤年,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钧敞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡麸粮,警方通過查閱死者的電腦和手機(jī)溉苛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弄诲,“玉大人愚战,你說我怎么就攤上這事∑胱瘢” “怎么了寂玲?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)梗摇。 經(jīng)常有香客問我拓哟,道長(zhǎng),這世上最難降的妖魔是什么伶授? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任断序,我火速辦了婚禮,結(jié)果婚禮上糜烹,老公的妹妹穿的比我還像新娘违诗。我一直安慰自己,他們只是感情好景图,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布较雕。 她就那樣靜靜地躺著,像睡著了一般挚币。 火紅的嫁衣襯著肌膚如雪亮蒋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天妆毕,我揣著相機(jī)與錄音慎玖,去河邊找鬼。 笑死笛粘,一個(gè)胖子當(dāng)著我的面吹牛趁怔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薪前,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼润努,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了示括?” 一聲冷哼從身側(cè)響起铺浇,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垛膝,沒想到半個(gè)月后鳍侣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丁稀,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年倚聚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了线衫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惑折,死狀恐怖授账,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惨驶,我是刑警寧澤矗积,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站敞咧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏辜腺。R本人自食惡果不足惜休建,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望评疗。 院中可真熱鬧测砂,春花似錦、人聲如沸百匆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)加匈。三九已至存璃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雕拼,已是汗流浹背纵东。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啥寇,地道東北人偎球。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辑甜,于是被迫代替她去往敵國(guó)和親衰絮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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