一、Buffer的属性
1. 介绍
Buffer缓冲区实际上就是一个数组 ,把数组的内容和信息包装成一个Buffer对象,它提供了一组访问这些信息的方法
2. 重要属性
capacity(容量) ① 指缓冲区可以存储多少个数据,容量在创建Buffer缓冲区时指定大小,创建之后不能再修改 ② 如果缓冲区满了,需要清空后才能继续写数据position(当前位置) ① 指缓冲区写入/读取的位置,刚刚创建Buffer对象后,position初始化为0,写入一个数据后,position就向后移动一个单元,它的最大值是capacity-1 ② 当Buffer从写模式切换到读模式,position会被重置为0,从Buffer的开始位置读取数据,每读取一个数据,position就向后移动一个单元limit(上限) ① 指第一个不能被读出或写入的位置,limit上限后面的单元既不能读也不能写 ② 在Buffer缓冲区的写模式下,limit表示能够写入多少个数据;在读取模式下,limit表示最多可以读取多少个数据mark(标记) ① 设置一个标记,可以调用mark()方法,把标记设置在position位置,当调用reset()方法时,就把position设置为mark标记的位置总之:0 <= mark <= position <= limit <= capacity
二、Buffer的常用API
1. 介绍
在NIO中关键的Buffer ① ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer,这些Buffer覆盖了能够通过I/O发送的所有基本类型byte、char、double、float、int、long、short等 ② 实际上使用较多的是ByteBuffer、CharBuffer
2. 重要方法
allocate() ① 每个缓冲区类都有一个静态方法allocate(capacity),可以创建一个指定容量的缓冲区put() ① 用于向缓冲区中存储数据get() ① 用于从缓冲区中读取数据compact() ① 当缓冲区中还有未读完的数据时,可以调用compact()方法进行压缩,将所有未读取的数据复制到Buffer的起始位置,把position设置到最后一个未读元素的后面,limit属性设置为capacitycapacity() ① 返回缓冲区的大小hasRemaining() ① 判断当前position后面是否还有可处理的数据,即判断position与limit之间是否还有数据可处理limit() ① 返回limit上限的位置mark() ① 设置缓冲区的标志位置,这个值只能在0~position之间,以后可以通过reset()方法返回到这个位置position() ① 返回position当前位置remaining() ① 返回当前position位置与limit之间的数量reset() ① 可以将position设置为mark标志位rewind() ① 将position设置为0,取消mark标志位clear() ① 清空缓冲区,仅仅是修改position标志为0,设置limit为capacity,缓冲区中数据还是存在的flip() ① 可以把缓冲区由写模式切换到读模式,先把limit设置为position位置,再把position设置为0
三、Buffer的常用API代码演示
1. Buffer的常用API操作
package buffer ;
import java. nio. CharBuffer ;
public class BufferApiDemo {
public static void main ( String [ ] args) {
CharBuffer buf = CharBuffer . allocate ( 12 ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. put ( '我' ) ;
buf. put ( '有' ) ;
buf. put ( '矿' ) ;
buf. put ( '你' ) ;
buf. put ( '爱' ) ;
buf. put ( '我' ) ;
buf. put ( '吗' ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. flip ( ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
System . out. println ( buf. get ( ) ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. put ( 'X' ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. mark ( ) ;
System . out. println ( buf. get ( ) ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. reset ( ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. compact ( ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. clear ( ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
System . out. println ( buf) ;
while ( buf. hasRemaining ( ) ) {
System . out. print ( buf. get ( ) ) ;
}
}
}
2. 缓冲区的批量传输
可以进行批量的操作,借助于数组,把缓冲区中的一块数据读到数组 中,也可以把数组中的部分内容保存到缓冲区中 批量传输时大小总是固定的,如果没有指定传输的大小,意味着把数组填满 当缓冲区的数据量不足以填满整个数组时,会抛出异常;在批量读取数据时,可以查询缓冲区中的剩余量 将小缓冲区中的数据填充到大的数组时,要指定缓冲区剩余量的长度
package buffer ;
import java. nio. CharBuffer ;
import java. util. Arrays ;
public class BufferBatchDemo {
public static void main ( String [ ] args) {
CharBuffer buf = CharBuffer . allocate ( 16 ) ;
buf. put ( "hello动力节点的蛙课网很牛B" ) ;
buf. flip ( ) ;
System . out. println ( buf) ;
char [ ] dst = new char [ 12 ] ;
CharBuffer remainingCharBuffer = buf. get ( dst) ;
System . out. println ( Arrays . toString ( dst) ) ;
System . out. println ( remainingCharBuffer) ;
buf. get ( dst, 0 , buf. remaining ( ) ) ;
System . out. println ( Arrays . toString ( dst) ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
buf. clear ( ) ;
while ( buf. hasRemaining ( ) ) {
int len = Math . min ( dst. length, buf. remaining ( ) ) ;
buf. get ( dst, 0 , len) ;
System . out. print ( new String ( dst, 0 , len) ) ;
}
System . out. println ( ) ;
char [ ] contents = { 'a' , 'b' , 'c' , 'd' } ;
buf. position ( 14 ) ;
buf. put ( contents, 0 , buf. remaining ( ) ) ;
buf. flip ( ) ;
System . out. println ( buf) ;
}
}
3. 缓冲区创建的两种方式
分配操作创建缓冲区 ① allocate()方法分配一个私有的、指定容量大小的数组来存储元素 ② CharBuffer buf1 = CharBuffer.allocate(16);包装操作创建缓冲区 ① 使用提供的数组作为存储空间来存储缓冲区的数据,不再分配其他空间 ② char[] myarray = new char[16];CharBuffer buf2 = CharBuffer.wrap(myarray); ③ 对数组myarray的任何修改,也会影响缓冲区对象总之 ① 不管是allocate()方法还是通过wrap()方法创建的缓冲区都是间接的,间接缓冲区会使用备份数组 ② hasArray()方法可以判断是否有一个可存取的备份数组,如果hasArray()返回true,可以通过array()方法返回缓冲区对象使用的备份数组的引用
package buffer ;
import java. nio. CharBuffer ;
import java. util. Arrays ;
public class BufferCreateDemo {
public static void main ( String [ ] args) {
CharBuffer buf1 = CharBuffer . allocate ( 16 ) ;
char [ ] myarry = new char [ 16 ] ;
CharBuffer buf2 = CharBuffer . wrap ( myarry) ;
buf2. put ( "hello" ) ;
buf2. flip ( ) ;
System . out. println ( buf2) ;
System . out. println ( Arrays . toString ( myarry) ) ;
myarry[ 0 ] = 'X' ;
buf2. position ( 0 ) ;
System . out. println ( buf2) ;
System . out. println ( Arrays . toString ( myarry) ) ;
if ( buf2. hasArray ( ) ) {
char [ ] arr2 = buf2. array ( ) ;
System . out. println ( Arrays . toString ( arr2) ) ;
}
}
}
4. 缓冲区的复制与分隔
package buffer ;
import java. nio. CharBuffer ;
public class BufferDuplicateDemo {
public static void main ( String [ ] args) {
CharBuffer buf = CharBuffer . allocate ( 16 ) ;
buf. put ( "hello" ) ;
System . out. println ( "capacity:" + buf. capacity ( ) + ",limit:" + buf. limit ( ) + ",position:" + buf. position ( ) ) ;
CharBuffer buf2 = buf. duplicate ( ) ;
System . out. println ( "capacity:" + buf2. capacity ( ) + ",limit:" + buf2. limit ( ) + ",position:" + buf2. position ( ) ) ;
buf2. flip ( ) ;
System . out. println ( buf2) ;
buf2. clear ( ) ;
buf2. put ( "NIOworld" ) ;
buf. flip ( ) ;
System . out. println ( buf) ;
buf2. position ( 3 ) ;
CharBuffer buf3 = buf2. slice ( ) ;
System . out. println ( "capacity:" + buf3. capacity ( ) + ",limit:" + buf3. limit ( ) + ",position:" + buf3. position ( ) ) ;
}
}
四、直接字节缓冲区
在硬盘中和操作系统中处理的数据都是01二进制,缓冲区中只有ByteBuffer字节缓冲区有资格参与I/O操作 Channel通道只能使用ByteBuffer作为它的参数 直接字节缓冲区通常是I/O操作是最好的选择,如果使用非直接字节缓冲区可能会导致性能损耗,如果向通道传递一个非直接字节缓冲区,通道可能会先创建一个临时的直接字节缓冲区,将非直接字节缓冲区的内容复制到这个临时的直接字节缓冲区中,使用临时直接字节缓冲区执行底层的I/O操作 创建直接字节缓冲区的成本要高于非直接字节缓冲区,因为它使用的内存是通过调用本地操作系统的代码分配的,绕过了JVM的堆栈 ByteBuffer.allocateDirect()方法创建直接字节缓冲区