NIO(Buffer和Channel)

NIO(New I/O)

  • NIO采用内存映射文件的方式来处理输入输出,它将文件或文件一段区域映射到内存中,这样就可以像访问内存一样来访问文件
  • 在标准I/O中,使用的是字节流和字符流,NIO使用的是通道(Channel)和缓冲区(Buffer)。
  • 数据总是从通道读入缓冲区,或从缓冲区写入通道

NIO主要有三大核心:

1. Buffer:可以看作是一个容器,其本质是一个数组缓冲区,读入或写出到Channel中的所有对象都会先放在Buffer中
2. Channel:是对传统的输入输出的模拟,在NIO中,所有的数据都需要通过通道流的形式传输
3. Selector(选择器):用于监听多个通道的事件(例如:连接打开、数据到达等),主要用于多线程处理
Buffer(缓冲器)

Java NIO中的Buffer用于和NIO中的Channel进行交互,交互时数据会从Channel读取到Buffer,或从Buffer写入到Channel中,如图:

NIO(Buffer和Channel)_第1张图片

从结构上说,Buffer类似一个数组,它可以保存多个类型相同的数据。从类型上说,Buffer是一个抽象类,其子类有ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer和ShortBuffer,最常用的是ByteBuffer和CharBuffer

Buffer的子类没有构造方法,因此不能通过构造方法来创建对象。创建Buffer对象,通常通过子类中的static XxxBuffer allocate(int capaction)方法来实现,例如创建一个容量为6的CharBuffer对象的语句:

CharBuffer charBuffer = CharBuffer.allocate(6);

Buffer中的三个概念需要了解:

  1. capacity(容量):缓冲区的容量表示该Buffer的最大数据容量,即最多可以存储多少数据。缓冲区的容量值不能为负数,也不能改变
  2. limit(界限):表示Buffer容器中不可读取的区域的第一个索引,即位于Buffer容器中索引为0到limit之间的区域都可以进行读取操作。缓冲区的limit值不能为负,也从不大于其容量
  3. position(位置):用于指定下一个可以被读取的缓冲区位置索引。新创建的Buffer对象,position的默认值为0,每进行一次读取或写入操作,position的值都会自动向后移动一步。

Buffer类中,定义很多方法

int capacity()   //获取缓冲区大小
int position()   //获取Buffer中position的值
Buffer position(int newPosition)    //设置Buffer的position,并返回位置被修改之后的Buffer对象
int limit()    //获取Buffer的limit的位置
Buffer limit(int newLimit)    //设置limit的值,并返回一个新的limit缓冲区对象
Buffer mark()    //设置Buffer的标记mark,只能在0到position之间做标记
Buffer reset()    //将此缓冲区的位置重置为先前标记的位置
Buffer clear()    //清除缓冲区,将position设置为0,limit设置为capacity
Buffer flip()    //反转缓冲区,先将limit设置为当前position位置,然后再将position位置设置为0
Buffer rewind()    //倒带缓冲区,将position位置设置为0,并取消设置的标记
int remaining()    //获取当前位置和界限之间的元素个数
boolean hasRemaining()       //判断当前位置position和limit之间是否还有元素

代码如下:

package com.Put;

import java.nio.CharBuffer;

public class IO {
    public static void main(String[] args) {
        //创建CharBuffer对象,并指定缓冲区的容量大小为6
        CharBuffer charBuffer = CharBuffer.allocate(6);
        System.out.println("容量:"+charBuffer.capacity());
        System.out.println("界限值:"+charBuffer.limit());
        System.out.println("初始值"+charBuffer.position());
        //向CharBuffer对象中放入3个元素
        charBuffer.put("x");
        charBuffer.put("y");
        charBuffer.put("z");
        System.out.println("加入元素后的界限值:"+charBuffer.limit());
        System.out.println("加入元素后的位置:"+charBuffer.position());
        //执行flip()方法
        charBuffer.flip();
        System.out.println("执行flip()方法后的界限值:"+charBuffer.limit());
        System.out.println("执行flip()方法后的位置:"+charBuffer.position());
        //取出第一个元素
        System.out.println("取出第一个元素为:"+charBuffer.get());
        System.out.println("取出后的界限值:"+charBuffer.limit());
        System.out.println("取出后的位置:"+charBuffer.position());
        //执行clear()方法
        charBuffer.clear();
        System.out.println("执行clear()后的界限值:"+charBuffer.limit());
        System.out.println("执行clear()后的位置:"+charBuffer.position());
        //取出第一个元素
        System.out.println("取出第一个元素为:"+charBuffer.get(0));
        System.out.println("取出后的界限值:"+charBuffer.limit());
        System.out.println("取出后的位置:"+charBuffer.position());
    }
    
}

运行结果:

容量:6
界限值:6
初始值0
加入元素后的界限值:6
加入元素后的位置:3
执行flip()方法后的界限值:3
执行flip()方法后的位置:0
取出第一个元素为:x
取出后的界限值:3
取出后的位置:1
执行clear()后的界限值:6
执行clear()后的位置:0
取出第一个元素为:x
取出后的界限值:6
取出后的位置:0
Channel(通道)
  • Channel是一个接口对象,它类似与传统的流对象,但与传统的流对象右有些不同,如下:
    1. Channel可以异步执行I/O读写操作
    2. Channel的读写操作是双向的,既可以从Channel中读取数据,又可以写数据到Channel,而流的读写通常是都是单向的
    3. Channel可以直接将指定的文件的部分或者全部映射成Buffer
    4. Channel只能与Buffer进行交互,程序不能直接读写Channel中的数据

Channel接口的实现类很多,主要讲解FileChannel的使用。Channel对象通过传统的I/O的getChannel()方法来获取对应的Channel,不同的流获取的Channel是不同的。如FileInputStream和FileOutputStream获取的是FileChannel,同时还可以使用RandomAccseeFile获取该对象。

FileChannel常用的方法:

通过FileChannel类的transferTo(long position,long count,WritableByteChannel target)方法实现了整个文件的拷贝,第一个参数表示所需转移文件的起始位置,这里表示从0开始;第二个参数表示要传输的最大字节数,这里通过size()方法获取了文件的字节数;第三个参数表示目标通道,即要传输到的位置。最后文件拷贝完成后,关闭所有资源

package com.Put;

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

public class IO {
    public static void main(String[] args) throws Exception {
        //创建RandomAccessFile对象,并指定源文件
        RandomAccessFile infile = new RandomAccessFile("E:\\Idea\\JavaSE\\重新学习\\resource\\985.png","rw");
        //获取读取文件FileChannel通道
        FileChannel inchannel = infile.getChannel();
        //创建RandomAccessFile对象,并指定目标文件
        RandomAccessFile outfile = new RandomAccessFile("E:\\Idea\\JavaSE\\重新学习\\target\\211.png","rw");
        //获取复制目标文件FileChannel通道
        FileChannel outchannel = outfile.getChannel();
        //使用TransferTo进行整体复制
        long transferTo = inchannel.transferTo(0, inchannel.size(), outchannel);

        if (transferTo>0){
            System.out.println("复制成功");
         }
        infile.close();
        outfile.close();
        inchannel.close();
        outchannel.close();
    }

}

你可能感兴趣的:(java)