盘一盘 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方法其实是个障眼法,并没有清除之前数据内容。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章