我们知道,Java中的NIO实际上使用的是多种IO模型中的IO多路复用策略,在NIO中,引入了Buffer缓冲区,Channel通道,Selector选择器三个概念,现在先看一下Buffer缓冲区的一些基本知识。
NIO的Buffer本质上是一个内存块,既可以写入数据,也可以从中读取数据,Java NIO中代表缓冲区的Buffer类是一个抽象类,位于java.nio包中。
NIO的内部是一个内存块(数组),与普通的内存块(Java数组)不同的是,NIO Buffer对象提供了一系列有效地方法,用来进行写入和读取的交替访问。
在NIO中,有8种缓冲区类型,分别是 ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer,MappedByteBuffer。前7种Buffer类型覆盖了能在IO中传输的所有Java基本数据类型,第8种类型是一种专门用于内存映射的ByteBuffer类型,不同的Buffer子类可以操作的数据类型能够通过名称进行判断,比如IntBuffer只能操作Integer类型的对象。
为了记录读写的状态和位置,Buffer类额外提供了一些重要的属性,其中有四个十分重要的成员属性capacity,position,limit,mark。
capacity属性表示Buffer内部容量的大小,一旦写入对象的数量超过了capacity,缓冲区满了,就不能再写入了。
capacity一旦初始化,就不能再改变,原因是Buffer类的对象在初始化的时候会按照capacity分配内部数组的内存,在数组内存被分配完毕后,就不能再改变大小了。
capacity并不是指内部内存块byte[]数组的字节数量,而是指能写入的数据对象的限制数量。
position属性的值和缓冲区的读写模式有关,在不同的模式下,position属性值的含义是不同的,当缓冲区的读写模式发生改变时,position值会进行相应的调整。
当新建了一个缓冲区示例后,缓冲区默认处于写模式,在数据写入完毕之后,可以使用**flip()**方法将缓冲区切换为读模式。
在从写模式切换到读模式的过程中,position和limit的属性值会发生变化,limit的值设置为写模式时的position的值,表示可以读取的最大数据的位置,position由原来的写入位置变为新的可读位置,也就是0,表示可以从头开始读取。
limit属性表示可以写入或者读取的最大位置,具体含义也和读写的模式有关。
在写模式下,初始化时limit的值会被设置为缓冲区的capacity值,表示可以将缓冲区的容量写满。
在读模式下,limit的值被设置为写模式下的position的值,表示最多能从缓冲区中读取多少数据。
在缓冲区的操作过程中,可以将当前的position值临时存入mark属性中,在需要的时候,再从mark中取出暂存的标记值,恢复到position位置开始处理。
在使用Buffer实例之前,我们需要先获取Buffer子类的实例对象,并且分配内存空间,在获取实例对象时,并不是使用子类的构造器来创建,而是使用子类的allocate()方法。
从实例中可以看出,一个缓冲区在创建之后默认是处于写模式的,position的属性值为0,capacity和limit的属性值相同,都为20。
在缓冲区创建完毕后,就可以写入对象,如果要把对象写入到缓冲区中,就需要调用put()方法,要求写入的数据类型和缓冲区的数据类型保持一致。
从结果可以看出,写入了5个元素之后,position的属性值就变为了5,指向了第6个可以写入的位置,而capacity和limit两个属性并没有发生变化。
在缓冲区写入完数据之后,还不能从中直接读取数据,需要将缓冲区切换为读模式,使用**flip()**翻转方法。
在翻转之后,position和limit属性的值都发生了变化,limit变为了写模式下position的值,表示最大的读取位置是5,而position的值变为0,表示从头开始读取。最后,清楚mark的值,因为mark储存的是写模式下的临时位置,发生翻转之后需要清空,防止发生混乱。
可以看一下源码:
那当数据读取完毕之后,如何从读模式切换到写模式呢,可以调用clear()方法清空或者调用compact()方法进行压缩。
切换到读模式之后,就可以从缓冲区读取数据了,可以调用get()方法读取数据。并且缓冲区的属性也会相应的发生变化。
从以上实例可以看出,读操作会改变position的值,而不会改变limit的值,当position的值和limit的值相等时,表示所有数据读取完成,如果继续读取,则会抛出BufferUnderflowException异常。
在读取完毕之后是不能立即写入数据的,必需要调用clear()方法清空或者调用compact()方法进行压缩。
那么已经读取完的缓冲区是否可以重复读呢,答案是可以的,需要使用rewind()方法。
rewind()也叫倒带,就像磁带一样,倒回去重新听一遍。
rewind()方法主要是调整了缓冲区的position和mark属性的值。
将position设置为0,limit保持不变,mark被清理,清除掉之前存储的位置。
这两个方法是配套使用的,mark()方法将当前position设置到mark属性中,reset()将mark属性的值恢复到position中,以便于从这个位置重复调用。
使用reset()方法之后,position的值为2,此时再去读取缓冲区,得到的结果为2,3,4。
在读模式下,调用clear()方法可以将缓冲区从读模式切换为写模式。
此时position的值被清零,limit的值变为capacity的值,可以一直写入,知道缓冲区满。
下篇文章总结一下Channel类,如有帮助,请点赞,谢谢!
本文发布于:2024-02-02 08:09:19,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170683256042489.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |