NIO(一):Buffer缓冲区

一.NIO与IO:

 IO:  一般泛指进行input/output操作(读写操作),Java IO其核心是字符流(inputstream/outputstream)和字节流(reader/writer)做为基本进行操作,只能做单向操作,而IO的读写方式采用流的方式进行读写操作,如图所示

 

 

 

对于NIO既可以说是(NEW NIO) 也是(NON Blocking IO),为什么说他的性能和效率高于IO流,其的传输方式采用块传输方式,也就是使用缓冲区(buffer),使用channel(通道)进行双向传输。

 

 

 其关键三个API为:

channel(通道): 实现数据的双向输出

Buffer(缓冲区):用于存储临时的读写数据

Selector(选择器) 若干客户端在Selector中注册自己,若干channel注册到Selector中,通过选择操作选出就绪的键,通道线程来实现少量线程的为多个客户端服务

 

二.Buffer缓冲区

什么是缓冲区?Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。

 在内存中开辟一段连续的区域,进行临时存储,实际上缓冲区为一个数组,在Buffer中可以进行存储的数据类型为:

  • CharBuffer

  • FloatBuffer

  • IntBuffer

  • DoubleBuffer

  • ShortBuffer

  • LongBuffer

  • ByteBuffer

 这些基本的实现都继承自Buffer类。

Buffer中定义了4个属性为:

1     private int mark = -1;   //此属性将在随后说到
2     private int position = 0;
3     private int limit;
4     private int capacity;

 

其中:

 

 
不变性(以1,2,3,4为从低到高排序)

position

即将被读或写的位置

2

limit

 限制最大取出值/放入值,注意:

position < limit

3

capacity

 缓冲区中可以存储的最大容量

4

mark

 标记位置,用于标记某一次的读取/写入的位置

1

 三.以ByteBuffer为例

在进行读写操作前需要开辟一个区域,对ByteBuffer进行初始化操作。

继承体系:

ByteBuffer bf = ByteBuffer.allocate(1024);

 

在使用allocate初始化中,进入方法中,当容量为负时,返回一个异常,反之初始化一个HeapByteBuffer

 public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

 并进行对属性进行赋值并创建一个相应容量的Byte数组

HeapByteBuffer(int cap, int lim) {            // package-private

        super(-1, 0, lim, cap, new byte[cap], 0);
        /*
        hb = new byte[cap];
        offset = 0;
        */
    }
 ByteBuffer(int mark, int pos, int lim, int cap,   // package-private
                 byte[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

 

由此完成便开辟一个区域用来存储数据

使用position 方法,limit方法,capacity方法查询相关信息,根据上面例子创建一个1024容量的缓冲区

    //存储位置
        System.out.println("position:---"+bf.position());
        //存储限制大小
        System.out.println("limit:---"+bf.limit());
        //存储容量
        System.out.println("capacity:---"+bf.capacity());

 

 

 此时position所指向0的位置。limiit所指向1024的位置。

 

 

 随后在进行写操作时使用put进行写入操作,

1         //为缓存区中存入数据
2         System.out.println("-------------put写入-------------");
3         //定义一个数据
4         String str= "123456";
5         bf.put(str.getBytes()); //将结果存储到新的字节数组中。
6         System.out.println("position:---"+bf.position());
7         System.out.println("limit:---"+bf.limit());
8         System.out.println("capacity:---"+bf.capacity());

 

 此时:

 

 那么position是如何得知的那?

通过在HeapByteBuffer中position初始化为0+数据转化为byte数组后的长度加起来得到position的值。并记录

写入时,position < limit    此时limit没有变化,

1 public ByteBuffer put(byte[] src, int offset, int length) {
2 
3         checkBounds(offset, length, src.length);
4         if (length > remaining())
5             throw new BufferOverflowException();
6         System.arraycopy(src, offset, hb, ix(position()), length);
7         position(position() + length);
8         return this;

 

 这就完成了数据的写入,当想在缓冲区中读取数据时,在需要先切换至读取状态,使用flip方法,在Buffer中的flip方法中 ,定义了将position赋给limit 限制读取的最大长度。并使position置为0

1  public final Buffer flip() {
2         limit = position;
3         position = 0;
4         mark = -1;
5         return this;
6     }

 进行切换状态

 1  //写入 后切换为读取状态
 2         System.out.println("-------------flip切换-------------");
 3         /*
 4         * 将limit = position
 5         *  令 position  = 0
 6         * */
 7         bf.flip();   //切换读取状态
 8         System.out.println("position:---"+bf.position());
 9         System.out.println("limit:---"+bf.limit());
10         System.out.println("capacity:---"+bf.capacity());

 

 

 

 

 

 此时:

 

 

 

切换完成后,便进行读的操作。

1 //切换成功后进行读取
2         System.out.println("-------------get读取-------------");
3         /*
4         * 从零开始读 ,读6个长度停止
5         * */
6         bf.get(str.getBytes(), 0, 6);
7         System.out.println("position:---"+bf.position());
8         System.out.println("limit:---"+bf.limit());
9         System.out.println("capacity:---"+bf.capacity());

 

 

 

 

 

 

 

 

你也可以并不止于读写,还可以清空缓冲区。使用clear方法

/**
     * Clears this buffer.  The position is set to zero, the limit is set to
     * the capacity, and the mark is discarded.
     *
     * <p> Invoke this method before using a sequence of channel-read or
     * <i>put</i> operations to fill this buffer.  For example:
     *
     * <blockquote><pre>
     * buf.clear();     // Prepare buffer for reading
     * in.read(buf);    // Read data</pre></blockquote>
     *
     * <p> This method does not actually erase the data in the buffer, but it
     * is named as if it did because it will most often be used in situations
     * in which that might as well be the case. </p>
     *
     * @return  This buffer
     */
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

 

 根据官方注释解释,这里清除并非真正的清除数据,而是抹去了存在的痕迹。使它被遗忘掉。数据也是可读的。

 1 //清空缓存区
 2         System.out.println("-------------clear清空-------------");
 3 
 4         bf.clear();
 5         System.out.println("position:---"+bf.position());
 6         System.out.println("limit:---"+bf.limit());
 7         System.out.println("capacity:---"+bf.capacity());
 8         //查看实际是否存在数据,并未被清除,只是被遗忘了。
 9         System.out.println((char)bf.get(0));
10         System.out.println((char)bf.get(1));

 

 

 

此时:

 

在Buffer中还提供了一些方法如下:

rewind()

对缓冲区存入的数据重复读

remaining()

统计属性间的元素数limit - position

hasRemaining()

 统计当前位置中是否还有数据

   

 

 

 

 

 

 

 

 

部分引用内容载自:https://www.cnblogs.com/imstudy/p/11108085.html

 

posted @ 2020-07-23 15:46  CLLOVER  阅读(484)  评论(0编辑  收藏  举报