• 盘一盘 NIO (一)—— Buffer源码解析


    Buffer是个啥?

    Buffer 即缓冲区,用来暂存输入输出数据的区域。Buffer对象是一份固定数量的数据的容器,实质上是一个数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,还可以跟踪系统的读/写进程。
     
    在 Java传统 IO 中,数据直接写入或者将数据直接读到 Stream 对象中。
    而在 NIO 中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
    NIO中的Buffer主要用于与NIO通道(channel)进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
     

    继承关系图

    Buffer作为一个容器用来存储数据的,根据存储数据类型的不同,分为以下七种:
    ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer

    主要属性

    // 标记、位置、限制、容量遵守以下不变式:
    // 0<=mark<=position<=limit<=capacity
    
    // 标记是一个位置索引,可以在之后设置位置为标记值再次读写。
    private int mark = -1;
    
    // 位置,表示缓冲区中正在操作数据的位置。
    // 可以把position理解成一个指针,指向的位置就是操作的位置。
    private int position = 0;
    
    // 界限,表示缓冲区中可以操作数据的大小。
    private int limit;
    
    // 容量,表示缓冲区中最大存储数据的容量。一旦声明,不可改变。
    private int capacity;
    
    // 指向缓冲区的地址  
    long address;

    构造方法

    // 创建一个具有给定标记、位置、限制和容量的新缓冲区
    Buffer(int mark, int pos, int lim, int cap) {
        // 缓冲区的初始化容量不能小于0
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        // 初始化缓冲区容量
        this.capacity = cap;
        // 初始化缓冲区限制,设置最新的缓冲区Buffer  
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }

    关键方法解析

    position方法:设置当前操作位置

    public final Buffer position(int newPosition) {
        // 当前操作位置不能超过limit大小,不能小于0
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        // 如果标记位置大于正在操作位置,那么将mark设置为-1。
        if (mark > position) mark = -1;
        return this;
    }

    limit方法:设置缓冲区限制

    public final Buffer limit(int newLimit) {
        // newLimit大小不能超过缓冲区最大容量capacity,不能小于0
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        // 正在操作数据的位置不能大于界限
        if (position > limit) position = limit;
        // 如果标记位置大于界限,那么将mark设置为-1。
        if (mark > limit) mark = -1;
        return this;
    }

    reset方法:将缓冲区Buffer的位置position设置为以前临时备忘变量mark存储的位置

    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }
    clear方法:清除缓冲区,clear方法并没有将之前的数据真的清除,当读数据前调用flip方法后,limit会限制不让使用旧数据。
    public final Buffer clear() {
        position = 0;
        // 虽然将limit数值设置为最大容量,只是为了正常使用buffer
        limit = capacity;
        mark = -1;
        return this;
    }

    flip方法: 反转此缓冲区,在进行写完去读的时候使用,说白了,让你开始读的时候从初始位置0开始。

    public final Buffer flip() {
        // 将position置为0,其实就是切换读写模式
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    rewind方法:重绕此缓冲区,在通道写入或者获取之前调用

    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

    remaining方法:返回当前的缓冲区Buffer中剩余元素的数量

    public final int remaining() {
        return limit - position;
    }

    总结

    1、Buffer中所有的方法操作,都遵守以下不变式:
     0<=mark<=position<=limit<=capacity
    2、flip方法,让数据开始读的时候从初始位置0开始。
    3、clear方法其实是个障眼法,并没有清除之前数据内容。
  • 相关阅读:
    设计模式相关,单例模式!
    混合App交互相关,jsBridge的原理
    Vue相关,插槽怎么用!
    Mysql相关,查询功能整理
    图文并茂讲解进程与线程
    线程与进程的区别及其通信方式
    算法,十进制数转任意进制数
    JavaScript,leetcode第198题,打家劫舍
    2.4G无线控制器附加AT2401C功放IC增加距离
    超低功耗WiFi :ESP8089
  • 原文地址:https://www.cnblogs.com/LemonFive/p/11394894.html
Copyright © 2020-2023  润新知