类ByteBuffer是Java nio程序经常会用到的类,也是重要类 ,我们通过源码分析该类的实现原理。

一.ByteBuffer类的继承结构

public abstract class ByteBuffer
extends Buffer
implements Comparable<ByteBuffer>

ByteBuffer的核心特性来自Buffer

二. ByteBuffer和Buffer的核心特性
A container for data of a specific primitive type. 用于特定基本类型数据的容器。
子类ByteBuffer支持除boolean类型以外的全部基本数据类型。

补充,回顾Java的基本数据类型

Java语言提供了八种基本类型,六种数字类型(四个整数型,两个浮点型),一种字符类型,一种布尔型。

1、整数:包括int,short,byte,long
2、浮点型:float,double
3、字符:char
4、布尔:boolean

类型    大小 最小值   最大值
byte    8-bit -128   +127
short  16-bit -2^15   +2^15-1
int       32-bit -2^31   +2^31-1
long    64-bit -2^63   +2^63-1
float    32-bit IEEE754   IEEE754
double 64-bit IEEE754   IEEE754
char    16-bit Unicode 0 Unicode 2^16-1
boolean ----- -----   ------     

本质上,Buffer也就是由装有特定基本类型数据的一块内存缓冲区和操作数据的4个指针变量(mark标记,position位置, limit界限,capacity容量)组成。不多说,上源码:

public abstract class Buffer {// Invariants: mark <= position <= limit <= capacityprivate int mark = -1;private int position = 0;private int limit;private int capacity;......
}public abstract class ByteBufferextends Bufferimplements Comparable<ByteBuffer>
{// These fields are declared here rather than in Heap-X-Buffer in order to// reduce the number of virtual method invocations needed to access these// values, which is especially costly when coding small buffers.//final byte[] hb;   // Non-null only for heap buffersfinal int offset;boolean isReadOnly;   // Valid only for heap buffers......
}

其中,字节数组final byte[] hb就是所指的那块内存缓冲区。

Buffer缓冲区的主要功能特性有:
a.Transferring data  数据传输,主要指可通过get()方法和put()方法向缓冲区存取数据,ByteBuffer提供存取除boolean以为的全部基本类型数据的方法。

b.Marking and resetting  做标记和重置,指mark()方法和reset()方法;而标记,无非是保存操作中某个时刻的索引位置。

c.Invariants 各种指针变量

d.Clearing, flipping, and rewinding 清除数据,位置(position)置0(界限limit为当前位置),位置(position)置0(界限limit不变),指clear()方法, flip()方法和rewind()方法。

e.Read-only buffers 只读缓冲区,指可将缓冲区设为只读。

f.Thread safety 关于线程安全,指该缓冲区不是线程安全的,若多线程操作该缓冲区,则应通过同步来控制对该缓冲区的访问。

g.Invocation chaining 调用链, 指该类的方法返回调用它们的缓冲区,因此,可将方法调用组成一个链;例如:
 b.flip();
 b.position(23);
 b.limit(42);
等同于
 b.flip().position(23).limit(42);

三.ByteBuffer的结构

ByteBuffer主要由是由装数据的内存缓冲区和操作数据的4个指针变量(mark标记,position位置, limit界限,capacity容量)组成。
内存缓冲区:字节数组final byte[] hb;
ByteBuffer的主要功能也是由这两部分配合实现的,如put()方法,就是向数组byte[] hb存放数据。

    ByteBuffer bb = ByteBuffer.allocate(10); // 向bb装入byte数据bb.put((byte)9);

底层源码的实现如下

class HeapByteBufferextends ByteBuffer
{......public ByteBuffer put(byte x) {hb[ix(nextPutIndex())] = x;return this;}......final int nextPutIndex() {    if (position >= limit)throw new BufferOverflowException();return position++;}......
}

如上所述,bb.put((byte)9);执行时,先判断position 是否超过 limit,否则指针position向前移一位,将字节(byte)9存入position所指byte[] hb索引位置。

get()方法相似;

    public byte get() {return hb[ix(nextGetIndex())];}

4个指针的涵义

position:位置指针。微观上,指向底层字节数组byte[] hb的某个索引位置;宏观上,是ByteBuffer的操作位置,如get()完成后,position指向当前(取出)元素的下一位,put()方法执行完成后,position指向当前(存入)元素的下一位;它是核心位置指针。

mark标记:保存某个时刻的position指针的值,通过调用mark()实现;当mark被置为负值时,表示废弃标记。

capacity容量:表示ByteBuffer的总长度/总容量,也即底层字节数组byte[] hb的容量,一般不可变,用于读取。

limit界限:也是位置指针,表示待操作数据的界限,它总是和读取或存入操作相关联,limit指针可以被  改变,可以认为limit<=capacity。

ByteBuffer结构如下图所示

四. ByteBuffer的关键方法实现

1.取元素

    public abstract byte get();//HeapByteBuffer子类实现public byte get() {return hb[ix(nextGetIndex())];}//HeapByteBuffer子类方法final int nextGetIndex() {                if (position >= limit)throw new BufferUnderflowException();return position++;} 

 2.存元素

   public abstract ByteBuffer put(byte b);//HeapByteBuffer子类实现public ByteBuffer put(byte x) {hb[ix(nextPutIndex())] = x;return this;}

3.清除数据

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

可见,对于clear()方法,ByteBuffer只是重置position指针和limit指针,废弃mark标记,并没有真正清空缓冲区/底层字节数组byte[] hb的数据;
    ByteBuffer也没有提供真正清空缓冲区数据的接口,数据总是被覆盖而不是清空。
    例如,对于Socket读操作,若从socket中read到数据后,需要从头开始存放到缓冲区,而不是从上次的位置开始继续/连续存放,则需要clear(),重置position指针,但此时需要注意,若read到的数据没有填满缓冲区,则socket的read完成后,不能使用array()方法取出缓冲区的数据,因为array()返回的是整个缓冲区的数据,而不是上次read到的数据。

4. 以字节数组形式返回整个缓冲区的数据/byte[] hb的数据

    public final byte[] array() {if (hb == null)throw new UnsupportedOperationException();if (isReadOnly)throw new ReadOnlyBufferException();return hb;}

5.flip-位置重置

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

socket的read操作完成后,若需要write刚才read到的数据,则需要在write执行前执行flip(),以重置操作位置指针,保存操作数据的界限,保证write数据准确。   
    
 6.rewind-位置重置

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

Rewinds this buffer. The position is set to zero and the mark is discarded.
  和flip()相比较而言,没有执行limit = position;

7.判断剩余的操作数据或者剩余的操作空间

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

常用于判断socket的write操作中未写出的数据;

8.标记

    public final Buffer mark() {mark = position;return this;}

9.重置到标记

    public final Buffer reset() {int m = mark;if (m < 0)throw new InvalidMarkException();position = m;return this;}

五.创建ByteBuffer对象的方式

1.allocate方式

    public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}HeapByteBuffer(int cap, int lim) {  // package-privatesuper(-1, 0, lim, cap, new byte[cap], 0);/*hb = new byte[cap];offset = 0;*/}// Creates a new buffer with the given mark, position, limit, capacity,// backing array, and array offset//ByteBuffer(int mark, int pos, int lim, int cap, // package-privatebyte[] hb, int offset){super(mark, pos, lim, cap);this.hb = hb;this.offset = offset;}// Creates a new buffer with the given mark, position, limit, and capacity,// after checking invariants.//Buffer(int mark, int pos, int lim, int cap) { // package-privateif (cap < 0)throw new IllegalArgumentException();this.capacity = cap;limit(lim);position(pos);if (mark >= 0) {if (mark > pos)throw new IllegalArgumentException();this.mark = mark;}}

由此可见,allocate方式创建ByteBuffer对象的主要工作包括: 新建底层字节数组byte[] hb(长度为capacity),mark置为-1,position置为0,limit置为capacity,capacity为用户指定的长度。

2.wrap方式

 public static ByteBuffer wrap(byte[] array) {return wrap(array, 0, array.length);}public static ByteBuffer wrap(byte[] array,int offset, int length){try {return new HeapByteBuffer(array, offset, length);} catch (IllegalArgumentException x) {throw new IndexOutOfBoundsException();}}HeapByteBuffer(byte[] buf, int off, int len) { // package-privatesuper(-1, off, off + len, buf.length, buf, 0);/*hb = buf;offset = 0;*/}

wrap方式和allocate方式本质相同,不过因为由用户指定的参数不同,参数为byte[] array,所以不需要新建字节数组,byte[] hb置为byte[] array,mark置为-1,position置为0,limit置为array.length,capacity置为array.length。

       六、结论

由此可见,ByteBuffer的底层结构清晰,不复杂,源码仍是弄清原理的最佳文档。
读完此文,应该当Java nio的SocketChannel进行read或者write操作时,ByteBuffer的四个指针如何移动有了清晰的认识。

Java的NIO之ByteBuffer底层分析相关推荐

  1. Java面试学习笔记——MySQL底层分析

    参考视频链接:尚硅谷MySQL数据库高级,mysql优化,数据库优化(尚硅谷MySQL数据库高级,mysql优化,数据库优化_哔哩哔哩_bilibili) 参考笔记链接:(https://blog.c ...

  2. Java NIO使用及原理分析

    http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一些Java方面的开发,其中最重要的一块 ...

  3. Java NIO使用及原理分析(二)

    2019独角兽企业重金招聘Python工程师标准>>> 转载自:李会军•宁静致远 在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是 ...

  4. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  5. 【IO专栏】Java OIO NIO通信对比分析【002】

    在1.4版本之前,Java IO类库是阻塞IO:从1.4版本开始,引入了新的异步IO库,被称为Java new IO 类库,new IO 类库的目标,就是要让Java支持非阻塞IO,基于这个原因更多人 ...

  6. NIO 之 ByteBuffer实现原理

    相关文章 IO.NIO.AIO 内部原理分析 NIO 之 Selector实现原理 NIO 之 Channel实现原理 前言 Java NIO 主要由下面3部分组成: Buffer Channel S ...

  7. java之NIO(Channel,Buffer,Selector)

    java之NIO 1 什么是NIO Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API.NIO支持面向缓冲区的.基于通道的IO操作 ...

  8. Java中nio、bio和aio的区别

    前言:IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO.现在使用NIO的场景越来越多,很多网上的技术框架或多或少用到了NIO技术,譬如Tomcat.Jetty,还有基于 ...

  9. 死磕Java之NIO与IO

    死磕Java之NIO与IO 当学习Java NIO与IO时,你是否会有这样的想法:什么时候使用NIO,什么使用IO呢?本篇文章将会分析两者的不同,它们的用例,以及和影响代码的设计. 01 NIO与IO ...

最新文章

  1. linux命令积累!
  2. 转:C#读取Excel文件 (2009年9月28日)
  3. 删除字符问题(贪心)
  4. 【SpringBoot】使用Maven添加jQuery、bootstrap等依赖(WebJars)
  5. 利用深度卷积模型对巴拉科咖啡叶疾病进行分类
  6. java与数据库连接的几个步骤
  7. python 微信公众号发文章_Python 微信公众号文章爬取
  8. mappedBy的基本认识
  9. SpringBooot:Redis:根据两地经纬度计算距离
  10. 感受晋味新年俗 你的高铁票可享景区门票优惠
  11. 重装系统后安装并激活Office
  12. html640设计稿,移动设备分辨率(终于弄懂了为什么移动端设计稿总是640px和750px)...
  13. 给程序进行简单的加壳
  14. CSS复合选择器:后代选择器
  15. java hasfocus_说说Flutter中的无名英雄 —— Focus
  16. 使用Proxmox搭建私有云平台
  17. 用VML标记语言画图、动画制作
  18. ABAP AMDP数据库存储过程(数据源)
  19. 正则表达式匹配空格与换行
  20. matlab中h无穷状态反馈控制,求教 :用LMI方法求倒立摆H无穷状态反馈控制器程序...

热门文章

  1. 网络安全 基础之 Windows漏洞复现 (MS17-010)永恒之蓝
  2. 截止至2022/8/23,CUDA ToolKit对应Nvdia驱动
  3. 直播星加密将加速数字电视进程
  4. 502粘到手上变硬了怎么办_衣服粘上502胶水变很硬怎么办
  5. 高数复习(4)--格林公式的理解
  6. JAVA之序列化(Serialization)的理解
  7. Python爬虫 教程:IP池的使用
  8. 人体正常生理指标(太有用了)
  9. 连接器 rj45和光模块
  10. cnpm install常见错误