• 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

  • 相关阅读:
    vs要用报表功能
    汉字与字节
    php 实现excel导入导出
    php 判断是否是手机端和电脑端访问
    php getimagesize image_type_to_extension 等函数不能用
    php 处理base64图片信息
    php 百度编辑器ueditor-dev-1.5.0编译版细节流程
    react 可拖拽改变位置和大小的弹窗
    Java 进制转换
    Java自动装箱测试
  • 原文地址:https://www.cnblogs.com/CllOVER/p/13366733.html
Copyright © 2020-2023  润新知