JAVA基础之NIO01_缓冲区和通道

Java在1.5之后提供了新的IO通信框架,NIO和普通IO的区别是NIO是基于channel和Buffer来进行操作的,这和传统的IO是有一些区别的,传统的IO是基于管道流的方式进行数据传输,而NIO的数据首先需要添加到buffer中,之后通过channel来进行传输。

Buffer缓冲区

首先要学会Buffer缓冲区的基本用法,不同的数据类型都是自己的缓冲区,但是在NIO中比较通用的是ByteBuffer,通过allocate和allocateDirect来创建缓冲区,第一种缓冲区是在堆中创建,第二种缓冲区会在操作系统的内存中创建,第二种缓冲区占用的是系统的内存资源,创建和销毁都需要一定的开销,在使用channel的时候能够提高一定的效率。这里先介绍Buffer的用法

//创建了一个8个字节的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(8);

缓冲区中有四个比较有用的属性

  • capacity:缓冲区的大小,此时是8个字节
  • limit:缓冲区的目前的可存储量,如果不做任何操作,该值用来表示缓冲区当前的存储位置,如果存储之后超过这个值就不能再存储。
  • position:即将被读写的缓冲区的位置的索引。
  • mark:一个游标对象,使用mark()方法可以将mark设置到position的位置,使用reset()方法可以将position设置为mark的值。

下面通过代码来了解缓冲区

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

没有任何数据的时候capactiy是8,说明是缓冲区的总大小,limit是8,表示可以存储到8个字节的位置,position表示缓冲区当前可以直接写入的索引,下面写一些数据看看

//写入hello
buffer.put("hello".getBytes());
//此时capacity是8,limit是8,position是5
System.out.println(buffer.capacity()+","+buffer.limit()+","+buffer.position());
//还存在3个元素的数量
System.out.println(buffer.remaining());
//存储空间不够,报错
buffer.put("this".getBytes());

添加了数据之后limit的值不变,position的位置调整了,使用remaining可以获取还能存储的数量。下面我们来看limit的作用,limit主要是在读取的时候有用,要读取信息,可以将position设置到0,之后通过get方法来读取。nio中提供了flip()方法来反转缓冲区,反转之后limit会指向当前缓冲区的最大值。

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());
//再写入一个值,由于已经到了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并不会清空缓冲区
System.out.println((char)buffer.get());
buffer.clear();//清空

最后看看mark和reset,当在某个位置是调用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的数据,操作和IO类似,但多了将Buffer添加到Channel的步骤

public class TestChannel {
    public static void main(String[] args) {
        FileChannel fc = null;
        try {
            //创建文件管道
            fc = FileChannel.open(Paths.get("d:/test/01.txt"), StandardOpenOption.READ);
            //创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = 0;
            byte[] buf = new byte[1024];
            //只要能够从缓冲区中读取数据
            while((len=fc.read(buffer))>0) {
                //重置缓冲区
                buffer.flip();
                //从缓冲区读取到字节数组中
                buffer.get(buf,0,len);
                //输出字节数组的值
                System.out.println(new String(buf,0,len));
                //重置缓冲区
                buffer.clear();
            }
            fc.read(buffer);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fc!=null) fc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
}

以上程序实现了通过一个文件管道读数据,此时没有输入和输出概念,如果希望把一个文件读到另外一个文件中,需要再创建一个通道来写文件。

public class TestChannel02 {
    public static void main(String[] args) {
        FileChannel fin = null;
        FileChannel fout = null;
        ByteBuffer buf = null;
        try {
            //创建两个通道,一个读数据,一个写数据
            fin = FileChannel.open(Paths.get("d:/test/01.jpg"), StandardOpenOption.READ);
            //设置文件如果不存在就创建,并且可以进行写操作
             fout = FileChannel.open(Paths.get("d:/test/02.jpg"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
            //创建缓冲区来作为数据的中转
            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目前比较的优势在于网络通信中,下一部分将会讲解基于网络通信的NIO操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容