zoukankan      html  css  js  c++  java
  • java NIO Buffer 详解(1)

    1.java.io  最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具;

    2.java.nio 中拥有3个核心概念:

        Selector Channel, Buffer ,在java nio 中我们面向的是块(block)或是缓冲区(buffer) 编程 ;Buffer 本身就是一块内存,底层实现上,是个数组,

        数据的读写都是通过Buffer 来实现的,flip 进行状态的翻转,读写切换;

    java 的种原生数据类型中都有各自对应的Buffer  如:IntBuffer LongBuffer  ByteBuffer CharBuffer 等,没有boolean 

    Channel  是指可以向其写入数据或者是从中读取数据的对象,类似 io stream,所有数据读写是通过BUFFER  执行的,永远不会出现直接向Channel 写入数据的情况以及直接向channel 读取数据的情况;channel 是双向的,可以进行读写的操作;可以更好反映底层操作情况

    3. 关于NIO  Buffer 中3个重要状态属性含义:position:  limit: capacity:

      

    Buffer 源码解读:
    
       A container for data of a specific primitive type.  Buffer 是一个具体的原始类型(除去boolean)的数据容器
    
        
    
       A buffer is a linear, finite sequence of elements of a specific primitive type. Aside from its content, the essential properties of a  buffer are its capacity, limit, and position:
    
    
       buffer 是一个 线性的,限定的,序列元素的 具体原始类型,除了他的内容之外, 一个buffer 必不可少的特性就是 capacity(容量)。limit    .position这三部分
    
    
    
       A buffer's <i>capacity</i> is the number of elements it contains.
       The capacity of a buffer is never negative and never changes.   capacity 是 buffer 容器里元素的个数, 它从来不会是负数,也不会被改变,在我们初始化的时候就会被我们自己定义它的大小

      

        A buffer's <i>position</i> is the index of the next element to be
        * read or written. A buffer's position is never negative and is never
        * greater than its limit.

        position(位置) 是 读写下一元素的索引,position 也是从来不会是负数,从来不会大于limit

      

      

        

        <p> A buffer's <i>limit</i> is the index of the first element that should
        * not be read or written. A buffer's limit is never negative and is never
        * greater than its capacity. </p>

        limit(限定) 是不应该被读到或者被写到的元素的首位置, 它不会是负数,不会超过 capacity

     

    经过以上源码解读,可以看出 capacity 在读写过程中是个不变的值,在定义的时候就固定了。

    position 是随着channel 读写变换的

    limit 是一个限定,用来告诉 position 应该只能写的这里,只能读到这里

    比较就是 position < =limit <= capacity

    即:

    参数

    写模式   

    读模式

    position

    当前写入的单位数据数量。

    当前读取的单位数据位置。

    limit

    代表最多能写多少单位数据和容量是一样的。

    代表最多能读多少单位数据,和之前写入的单位数据量一致。

    capacity

    buffer 容量

    buffer 容量

    <p> There is one subclass of this class for each non-boolean primitive type.
      每一个非布尔的原始类型在这个class 里都会有一个子类


    <p> Transferring data 

    Each subclass of this class defines two categories of <i>get</i> and
    * <i>put</i> operations: </p>

    数据的传输 ,每一个子类都会定义 get 和put 两种方式操作

    *<p> <i>Relative</i> operations read or write one or more elements starting
    * at the current position and then increment the position by the number of
    * elements transferred. If the requested transfer exceeds the limit then a
    * relative <i>get</i> operation throws a {@link BufferUnderflowException}
    * and a relative <i>put</i> operation throws a {@link
    * BufferOverflowException}; in either case, no data is transferred. </p>


    相对的 操作读和写一个或者多个元素在目前的position(位置坐标)通过传递元素的数量增加这个position 位置
    如果请求超出了limit 的范围,相对的get 操作会抛出BufferUnderflowException 异常
    put 方法操作会抛出BufferOverflowException 异常
    无论发生何种情况,都没有数据传递


    * <p> <i>Absolute</i> operations take an explicit element index and do not
    * affect the position. Absolute <i>get</i> and <i>put</i> operations throw
    * an {@link IndexOutOfBoundsException} if the index argument exceeds the
    * limit. </p>

    绝对的操作 进行明确一个元素的索引,不会影响postion 位置,如果索引参数超出了limit 就会抛出 IndexOutOfBoundsException 异常

    
    

    标记与重置的概念

    * <h2> Marking and resetting </h2>
     *
     * <p> A buffer's <i>mark</i> is the index to which its position will be reset
     * when the {@link #reset reset} method is invoked.  The mark is not always
     * defined, but when it is defined it is never negative and is never greater
     * than the position.  If the mark is defined then it is discarded when the
     * position or the limit is adjusted to a value smaller than the mark.  If the
     * mark is not defined then invoking the {@link #reset reset} method causes an
     * {@link InvalidMarkException} to be thrown.
     
    标记与重置
    
    buffer 的 标记是position 将会被重置的索引,当reset 方法被调用的时候。这个标记不总是被
    定义的,但是当他定义的时候,他的值不应该是负数以及它不会超出position 的值,
    如果标记被定义,当position 或者limit 调整成了一个比标记还小的值,那么这个标记就会被丢弃(-1)
    如果标记没有被定义,但是你调用了reset 方法,将会抛出 InvalidMarkException 异常
    
    一句话,mark 是一个标记,当调用reset 方法时候,position 位置将会是mark 的标记的索引位置

    构建一个新的buffer,构建buffer 时候做了什么事情呢?

     * <p> A newly-created buffer always has a position of zero and a mark that is
     * undefined.  The initial limit may be zero, or it may be some other value
     * that depends upon the type of the buffer and the manner in which it is
     * constructed.  Each element of a newly-allocated buffer is initialized
     * to zero.
    
    
    一个新创建的buffer 总会有一个从0开始的position 以及 未被定义的mark(-1)
    初始的limit可能是0 或者是其他的值主要取决于你buffer 的类型和构造函数的方式
    新分配的缓冲区每一个元素初始化为零


    -------------例子详解------------


    我们以IntBuffer 为例,进行构造它的Buffer 对象

    
    

    IntBuffer buffer =IntBuffer.allocate(10);//分配一个容量为10的IntBuffer

    
    

    我们跟踪源码点进去,第一步,可以看出它去创建了HeapIntBuffer 对象,

    
    

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

    
    

    点进去 HeapIntBuffer,可以差不多看到刚开始构造时候,capacity = limit 值

    
    

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

    
    

    super(-1, 0, lim, cap, new int[cap], 0);

    
    

    }

    
    

    点进去super,看到会创建出一个容量为capacity的数组,所以说buffer 底层是数组,且初始化的position 为0 ,mark 为-1(即无效)

    
    

    IntBuffer(int mark, int pos, int lim, int cap, // package-private
    int[] hb, int offset)
    {
    super(mark, pos, lim, cap);
    this.hb = hb;
    this.offset = offset;
    }

     

    常用的方法文档解释

    clear 方法
    
     *   <li><p> {@link #clear} makes a buffer ready for a new sequence of
     *   channel-read or relative <i>put</i> operations: It sets the limit to the
     *   capacity and the position to zero.  </p></li>
    
        clear 方法 使buffer 准备以一个全新的方式进行 channel -read 操作或者相对的buffer put 操作
        它会将limit 值设置成buffer capacity的值,以及position 从0 开始;
        
        如下源码方法:可以看出clear 方法执行的操作。position 置位0 limit 等于 capacity ,清除标记-1 
    
      public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }



    ------例子详解------

    public static void main(String[] args) {


    //分配一个容量为10 IntBuffer 底层是数组
    IntBuffer buffer =IntBuffer.allocate(10);
    //在够着函数创建之后,就会有了capacity limit position 的 值了
    System.err.println("起始capacity值==>"+buffer.capacity());
    System.err.println("起始limit值==>"+buffer.limit());
    System.err.println("起始position值==>"+buffer.position());

    for(int i =0;i<buffer.capacity();i++) {
    //遍历在每一个buffer 节点 插入随机数
    buffer.put(new SecureRandom().nextInt(10));
    //打印当前的 capacity limit position 的 值;
    System.out.println("循环capacity值==>"+buffer.capacity());
    System.out.println("循环limit值==>"+buffer.limit());
    System.out.println("循环position值==>"+buffer.position());
    }

    //buffer clear 方法调用

    buffer.clear();

    System.err.println("clear 后capacity值==>"+buffer.capacity());
    System.err.println("clear 后limit值==>"+buffer.limit());
    System.err.println("clear 后position值==>"+buffer.position());

    //......再进行自己的put 操作

    }

     
    flip 方法
    
     *   <li><p> {@link #flip} makes a buffer ready for a new sequence of
     *   channel-write or relative <i>get</i> operations: It sets the limit to the
     *   current position and then sets the position to zero.  </p></li>
    
        clear 方法 使buffer 准备以一个全新的方式进行 channel -write操作或者相对的buffer get操作
        他会将limit 值设置为当前position 值,将position 设置为0
    
        如下源码方法:可以看出flip 方法执行的操作。
     
     public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }

    -------例子详解

    public static void main(String[] args) {

    //分配一个容量为10 IntBuffer 底层是数组
    IntBuffer buffer =IntBuffer.allocate(10);
    //在够着函数创建之后,就会有了capacity limit position 的 值了
    System.err.println("起始capacity值==>"+buffer.capacity());
    System.err.println("起始limit值==>"+buffer.limit());
    System.err.println("起始position值==>"+buffer.position());

    //为了看到limit 效果,这里设置循环5次
    for(int i =0;i<5;i++) {
    //遍历在每一个buffer 节点 插入随机数
    buffer.put(new SecureRandom().nextInt(10));
    //打印当前的 capacity limit position 的 值;
    System.out.println("循环capacity值==>"+buffer.capacity());
    System.out.println("循环limit值==>"+buffer.limit());
    System.out.println("循环position值==>"+buffer.position());
    }

    //buffer flip 方法调用

    buffer.flip();

    System.err.println("flip 后capacity值==>"+buffer.capacity());
    System.err.println("flip 后limit值==>"+buffer.limit());
    System.err.println("flip 后position值==>"+buffer.position());


    while(buffer.hasRemaining()) {//=position < limit的时候循环;

    System.out.println("buffer的值===>"+buffer.get());
    }



    }

     
    rewind(倒回) 方法;
    
    
     *   <li><p> {@link #rewind} makes a buffer ready for re-reading the data that
     *   it already contains: It leaves the limit unchanged and sets the position
     *   to zero.  </p></li>
     *
     * </ul>
    
        rewind 方法是在已经存在的容器里重新读取数据做好准备,他会保持limit 不改变并且使position 为0
    
        如下源码方法:可以看出rewind方法执行的操作。
    
    public final Buffer rewind() {
            position = 0;
            mark = -1;
            return this;
        }
        
        
        public static void main(String[] args) throws Exception {
            
            FileInputStream file = new FileInputStream("c:\demo.txt");
            //获取channel 对象
            FileChannel fileChannel=file.getChannel();
            //读写操作都会用到buffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(5112);
            //channel 读取数据到buffer
            fileChannel.read(byteBuffer);
            
            //状态翻转,切换为读
            byteBuffer.flip();
            
            while(byteBuffer.hasRemaining()) {
            
                byte b =byteBuffer.get();
                System.out.println((char)b);
            }
            
            file.close();
        }
    原创打造,多多指教
  • 相关阅读:
    vim编辑器下nginx.conf语法高亮
    Spring Boot 警告:An illegal reflective access operation has occurred
    Idea使用 MyBatis Generator 插件快速生成代码
    SpringBoot 自定义线程池处理异步任务
    idea 处理mybatis的mapper.xml警告提示信息让其不显示
    mybatis 报文档根元素 "mapper" 必须匹配 DOCTYPE 根 "null"
    zookeeper生成分布式自增ID
    Navicat卸载注册表 激活码
    mybatis自定义TypeHandler实现list转string
    Java创建ES索引实现
  • 原文地址:https://www.cnblogs.com/iscys/p/9658302.html
Copyright © 2011-2022 走看看