<p align="right">——日拱一卒,不期而至!</p>html
你好,我是彤哥,本篇是netty系列的第六篇。java
上一章咱们一块儿学习了Java NIO的核心组件Channel,它能够看做是实体与实体之间的链接,并且须要与Buffer交互,这一章咱们就来学习一下Buffer的特性。编程
Buffer用于与Channel交互时使用,经过上一章的学习咱们知道,数据从Channel读取到Buffer,或者从Buffer写入Channel。数组
Buffer本质上是一个内存块,能够向里面写入数据,或者从里面读取数据,在Java中它被包装成了Buffer对象,并提供了一系列的方法用于操做这个内存块。网络
为了更好地理解Buffer的数据结构,咱们必须熟悉它的三个经常使用属性:数据结构
在读模式和写模式下,position和limit的位置有所不一样,见下图:架构
Buffer做为一个存储块,是有固定大小的,这个固定大小咱们称做“容量”。app
当Buffer写满以后,须要先清空或者读取数据,才能继续写入新的数据。dom
写模式下,position从0开始,每写入一个单位的数据,position前进一位,position最大可到达(capacity-1)的位置。性能
当Buffer从写模式切换为读模式时,position将重置为0。读取数据时,一样地,position每读取一个单位,前进一位,此时,position最大可到达limit的位置(实际最大可读取的位置是(limit-1))。
写模式下,limit最大值等于capacity。
读模式下,limit最大值等于切换为读模式时position的值,本文来源工从号彤哥读源码。
> 这里可能有点绕,position相似于数组的下标,是从0开始的,limit表示最大能够读取或者写入的长度,capacity表示最大的容量,limit和capacity不是下标,相似于数组的长度,因此跟position比较须要-1。在写模式下,position指向的是下一个待写入的位置;在读模式下,position指向的是下一个待读取的位置。
Java NIO自带的Buffer类型有:
与基本类型同样,每一种Buffer的基本单位长度不同罢了。
其中,MappedByteBuffer是一种特殊的ByteBuffer,它使用内存映射的方式加载物理文件,并不会耗费同等大小的物理内存,是一种直接操做堆外内存的方式,读写性能比较高。
上面咱们学习了Buffer的数据结构以及经常使用的Buffer类型,它们怎么使用呢?常见的用法主要有四种:
public class FileChannelTest { public static void main(String[] args) throws IOException { // 从文件获取一个FileChannel FileChannel fileChannel = new RandomAccessFile("D:\\object.txt", "rw").getChannel(); // 分配一个Byte类型的Buffer ByteBuffer buffer = ByteBuffer.allocate(1024); // 将FileChannel中的数据读出到buffer中,-1表示读取完毕 // buffer默认为写模式,本文来源工从号彤哥读源码 // read()方法是相对channel而言的,相对buffer就是写 while ((fileChannel.read(buffer)) != -1) { // buffer切换为读模式 buffer.flip(); // buffer中是否有未读数据 while (buffer.hasRemaining()) { // 读取数据 System.out.print((char)buffer.get()); } // 清空buffer,为下一次写入数据作准备 // clear()会将buffer再次切换为写模式 buffer.clear(); } } }
要获取一个Buffer对象,必须先分配它,每一个Buffer类都有一个allocate()方法用于分配Buffer对象。
如下示例分配了一个容量为1024的ByteBuffer对象:
ByteBuffer buffer = ByteBuffer.allocate(1024);
下面是分配了一个容量为48的CharBuffer的对象:
CharBuffer buf = CharBuffer.allocate(48);
将数据写入Buffer有两种形式:
从Channel读入Buffer的示例以下:
int bytesRead = inChannel.read(buf); //读入Buffer
Buffer本身put()写入数据的示例以下:
buf.put(127);
固然,put()有不少不一样的类型,好比在特定位置写入,写入不一样类型的数据等等,能够在IDEA中按F12查看。
flip()方法用于将Buffer从写模式切换为读模式,position将切换到0位置,且limit将切换到刚才position的位置。
也就是说,position变成了可读数据的首位,limit表示能够读取的最大数据长度。
从Buffer中读取数据也有两种形式:
从Buffer写入Channel的示例以下:
// 本文来源工从号彤哥读源码 int bytesWritten = inChannel.write(buf);
调用Buffer本身的get()方法读取数据的示例以下:
byte aByte = buf.get();
固然,get()有不少不一样的类型,好比从特定的位置读取,读取不一样类型的数据等等,能够在IDEA中按F12查看。
rewind()方法会重置position为0,但limit保持不变,所以能够用来从新读取数据。一般是在从新读取数据以前调用。
clear()方法用于清空整个Buffer,并将Buffer从读模式切换回写模式,且position归位到0位置。
compact()方法用于清空已读取的数据,并将未读取的数据移至Buffer的头部,position的位置移动到从头开始计算的未读取的数据的下一个位置,它也会将Buffer从读模式切换回写模式。
mark()方法用于标记给定位置,而后能够在以后经过reset()方法从新回到mark的位置,示例以下:
buffer.mark(); //屡次调用buffer.get(),例如在解析过程当中。 buffer.reset(); //将位置从新设置为标记。
今天咱们学习了Java NIO核心组件Buffer,它常常跟Channel联合起来使用。讲到这里咱们一直在使用FileChannel在举例,那么它们到底跟网络编程有什么关系呢?请听下回分解。
http://tutorials.jenkov.com/java-nio/channels.html
最后,也欢迎来个人工从号彤哥读源码系统地学习源码&架构的知识。