JAVA基础之NIO01_缓冲区和通道

2018-03-27  本文已影响0人  孔浩

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是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操作。

上一篇下一篇

猜你喜欢

热点阅读