JAVA基礎之NIO01_緩沖區(qū)和通道

Java在1.5之后提供了新的IO通信框架呀洲,NIO和普通IO的區(qū)別是NIO是基于channel和Buffer來進行操作的兼贡,這和傳統(tǒng)的IO是有一些區(qū)別的,傳統(tǒng)的IO是基于管道流的方式進行數(shù)據(jù)傳輸吼具,而NIO的數(shù)據(jù)首先需要添加到buffer中赡勘,之后通過channel來進行傳輸。

Buffer緩沖區(qū)

首先要學會Buffer緩沖區(qū)的基本用法楼熄,不同的數(shù)據(jù)類型都是自己的緩沖區(qū)忆绰,但是在NIO中比較通用的是ByteBuffer,通過allocate和allocateDirect來創(chuàng)建緩沖區(qū)可岂,第一種緩沖區(qū)是在堆中創(chuàng)建错敢,第二種緩沖區(qū)會在操作系統(tǒng)的內(nèi)存中創(chuàng)建,第二種緩沖區(qū)占用的是系統(tǒng)的內(nèi)存資源缕粹,創(chuàng)建和銷毀都需要一定的開銷稚茅,在使用channel的時候能夠提高一定的效率纸淮。這里先介紹Buffer的用法

//創(chuàng)建了一個8個字節(jié)的緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(8);

緩沖區(qū)中有四個比較有用的屬性

  • capacity:緩沖區(qū)的大小,此時是8個字節(jié)
  • limit:緩沖區(qū)的目前的可存儲量亚享,如果不做任何操作咽块,該值用來表示緩沖區(qū)當前的存儲位置,如果存儲之后超過這個值就不能再存儲欺税。
  • position:即將被讀寫的緩沖區(qū)的位置的索引侈沪。
  • mark:一個游標對象,使用mark()方法可以將mark設置到position的位置峭竣,使用reset()方法可以將position設置為mark的值晃虫。

下面通過代碼來了解緩沖區(qū)

//此時capacity是8,limit是8,position是0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());

沒有任何數(shù)據(jù)的時候capactiy是8,說明是緩沖區(qū)的總大小哲银,limit是8扛吞,表示可以存儲到8個字節(jié)的位置,position表示緩沖區(qū)當前可以直接寫入的索引荆责,下面寫一些數(shù)據(jù)看看

//寫入hello
buffer.put("hello".getBytes());
//此時capacity是8,limit是8,position是5
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//還存在3個元素的數(shù)量
System.out.println(buffer.remaining());
//存儲空間不夠,報錯
buffer.put("this".getBytes());

添加了數(shù)據(jù)之后limit的值不變做院,position的位置調(diào)整了,使用remaining可以獲取還能存儲的數(shù)量键耕。下面我們來看limit的作用寺滚,limit主要是在讀取的時候有用,要讀取信息屈雄,可以將position設置到0,之后通過get方法來讀取酒奶。nio中提供了flip()方法來反轉緩沖區(qū),反轉之后limit會指向當前緩沖區(qū)的最大值杠氢。

buffer.flip();
//此時capacity是8,limit是5,position是0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
byte[] buf = new byte[buffer.limit()];
buffer.get(buf,0,buffer.limit());
System.out.println(new String(buf));
//讀完之后position的值和limit一樣
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//再寫入一個值另伍,由于已經(jīng)到了limit的位置,就不能再寫入了,寫入就會報錯
buffer.put((byte)('A'));

此時通過clear()可以重置position為0愕宋,而且設置limit為capacity中贝。

buffer.clear();
//capactiy為8,limit為8邻寿,position為0
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//可以讀取值,說明clear并不會清空緩沖區(qū)
System.out.println((char)buffer.get());
buffer.clear();//清空

最后看看mark和reset,當在某個位置是調(diào)用mark()方法誊涯,會將mark設置為當前的position蒜撮,此時可以通過reset回復到原來的mark的位置

buffer.put("abc".getBytes());
buffer.mark();//設置mark的標記,此時mark這個變量為position
buffer.put("yes".getBytes());//在abc之后加入yes
buffer.reset();//此時position會設置到mark
System.out.println((char)buffer.get());////讀取的值是y

Channel的講解

NIO中是通過Channel來傳輸buffer的數(shù)據(jù)段磨,操作和IO類似,但多了將Buffer添加到Channel的步驟

public class TestChannel {
    public static void main(String[] args) {
        FileChannel fc = null;
        try {
            //創(chuàng)建文件管道
            fc = FileChannel.open(Paths.get("d:/test/01.txt"), StandardOpenOption.READ);
            //創(chuàng)建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = 0;
            byte[] buf = new byte[1024];
            //只要能夠從緩沖區(qū)中讀取數(shù)據(jù)
            while((len=fc.read(buffer))>0) {
                //重置緩沖區(qū)
                buffer.flip();
                //從緩沖區(qū)讀取到字節(jié)數(shù)組中
                buffer.get(buf,0,len);
                //輸出字節(jié)數(shù)組的值
                System.out.println(new String(buf,0,len));
                //重置緩沖區(qū)
                buffer.clear();
            }
            fc.read(buffer);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fc!=null) fc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
}

以上程序實現(xiàn)了通過一個文件管道讀數(shù)據(jù),此時沒有輸入和輸出概念债蜜,如果希望把一個文件讀到另外一個文件中,需要再創(chuàng)建一個通道來寫文件儒洛。

public class TestChannel02 {
    public static void main(String[] args) {
        FileChannel fin = null;
        FileChannel fout = null;
        ByteBuffer buf = null;
        try {
            //創(chuàng)建兩個通道特姐,一個讀數(shù)據(jù)黍氮,一個寫數(shù)據(jù)
            fin = FileChannel.open(Paths.get("d:/test/01.jpg"), StandardOpenOption.READ);
            //設置文件如果不存在就創(chuàng)建,并且可以進行寫操作
             fout = FileChannel.open(Paths.get("d:/test/02.jpg"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
            //創(chuàng)建緩沖區(qū)來作為數(shù)據(jù)的中轉
            buf = ByteBuffer.allocate(1024);
            while((fin.read(buf))>0) {
                buf.flip();
                fout.write(buf);
                buf.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fin!=null) fin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fout!=null) fout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在NIO中提供了transferTo方法來快速將兩個通道進行轉換

public class TestChannel03 {

    public static void main(String[] args) {
        FileChannel fin = null;
        FileChannel fout = null;
        
        try {
            fin = FileChannel.open(Paths.get("d:/test/01.jpg"), StandardOpenOption.READ);
            fout = FileChannel.open(Paths.get("d:/test/03.jpg"), StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);
            //通過transferTo可以將兩個通道對接
            fin.transferTo(0, fin.size(), fout);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fin!=null) fin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fout!=null) fout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

以上就是NIO的基本操作,難度不大专执,NIO目前比較的優(yōu)勢在于網(wǎng)絡通信中,下一部分將會講解基于網(wǎng)絡通信的NIO操作攀痊。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苟径,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蟆盐,老刑警劉巖遭殉,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痹愚,居然都是意外死亡罗心,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門疾瓮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來飒箭,“玉大人,你說我怎么就攤上這事弦蹂。” “怎么了削祈?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵脑漫,是天一觀的道長。 經(jīng)常有香客問我优幸,道長,這世上最難降的妖魔是什么羹饰? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮笑旺,結果婚禮上刹碾,老公的妹妹穿的比我還像新娘。我一直安慰自己迷帜,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布冠胯。 她就那樣靜靜地躺著锦针,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奈搜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天焕盟,我揣著相機與錄音宏粤,去河邊找鬼。 笑死来农,一個胖子當著我的面吹牛崇堰,可吹牛的內(nèi)容都是我干的沃于。 我是一名探鬼主播海诲,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼饿肺,長吁一口氣:“原來是場噩夢啊……” “哼盾似!你這毒婦竟也來了雪标?” 一聲冷哼從身側響起溉跃,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撰茎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆粹,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡炫惩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年他嚷,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹋绽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筋蓖。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡粘咖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涂炎,到底是詐尸還是另有隱情,我是刑警寧澤唱捣,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布震缭,位于F島的核電站,受9級特大地震影響拣宰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巡社,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一晌该、第九天 我趴在偏房一處隱蔽的房頂上張望绿渣。 院中可真熱鬧燕耿,春花似錦、人聲如沸誉帅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亚再。三九已至,卻和暖如春针余,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背圆雁。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工伪朽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人烈涮。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像戈稿,于是被迫代替她去往敵國和親讶舰。 傳聞我的和親對象是個殘疾皇子鞍盗,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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