星球狂想战队|深度解读Netty:NIO那些不为人知的秘密( 四 )


Buffer在java.nio中被定义为抽象类:
属性
Buffer中有4个非常重要的属性:capacity、limit、position、mark
limit属性:代表Buffer可读可写的上限 。 写模式下:limit代表能写入数据的上限位置 , 这个时候limit=capacity读模式下:在Buffer完成所有数据写入后 , 通过调用flip()方法 , 切换到读模式 , 此时limit等于Buffer中实际已经写入的数据大小 。 因为Buffer可能没有被写满 , 所以limit<=capacityposition属性:代表读取或者写入Buffer的位置 。 默认为0 。 写模式下:每往Buffer中写入一个值 , position就会自动加1 , 代表下一次写入的位置 。 读模式下:每往Buffer中读取一个值 , position就自动加1 , 代表下一次读取的位置 。
mark属性:代表标记 , 通过mark()方法 , 记录当前position值 , 将position值赋值给mark , 在后续的写入或读取过程中 , 可以通过reset()方法恢复当前position为mark记录的值 。这几个重要属性讲完 , 我们可以再来回顾下:
0?mark?position?limit?capacity
现在应该很清晰这几个属性的关系了~
Buffer常见操作
创建Buffer
allocate(intcapacity)ByteBufferbuffer=ByteBuffer.allocate(1024);intcount=channel.read(buffer);例子中创建的ByteBuffer是基于堆内存的一个对象 。
wrap(array)wrap方法可以将数组包装成一个Buffer对象:
ByteBufferbuffer=ByteBuffer.wrap("helloworld".getBytes());channel.write(buffer);allocateDirect(intcapacity)通过allocateDirect方法也可以快速实例化一个Buffer对象 , 和allocate很相似 , 这里区别的是allocateDirect创建的是基于堆外内存的对象 。
堆外内存不在JVM堆上 , 不受GC的管理 。 堆外内存进行一些底层系统的IO操作时 , 效率会更高 。
Buffer写操作
Buffer写入可以通过put()和channel.read(buffer)两种方式写入 。
通常我们NIO的读操作的时候 , 都是从Channel中读取数据写入Buffer , 这个对应的是Buffer的写操作 。
Buffer读操作
Buffer读取可以通过get()和channel.write(buffer)两种方式读入 。
还是同上 , 我们对Buffer的读入操作 , 反过来说就是对Channel的写操作 。 读取Buffer中的数据然后写入Channel中 。
rewind():重置position位置为0 , 可以重新读取和写入buffer , 一般该方法适用于读操作 , 可以理解为对buffer的重复读 。 publicfinalBufferrewind(){position=0;mark=-1;returnthis;}flip():很常用的一个方法 , 一般在写模式切换到读模式的时候会经常用到 。 也会将position设置为0 , 然后设置limit等于原来写入的position 。 publicfinalBufferflip(){limit=position;position=0;mark=-1;returnthis;}clear():重置buffer中的数据 , 该方法主要是针对于写模式 , 因为limit设置为了capacity , 读模式下会出问题 。 publicfinalBufferclear(){position=0;limit=capacity;mark=-1;returnthis;}mark()&reset():mark()方法是保存当前position到变量markz中 , 然后通过reset()方法恢复当前position为mark , 实现代码很简单 , 如下:publicfinalBuffermark(){mark=position;returnthis;}publicfinalBufferreset(){intm=mark;if(m<0)thrownewInvalidMarkException();position=m;returnthis;}常用的读写方法可以用一张图总结一下: