前面介绍了文件通道的读写操做,其中用到字节缓存ByteBuffer,它是位于通道内部的存储空间,也是通道惟一可用的存储形式。ByteBuffer有两种构建方式,一种是调用静态方法wrap,根据输入的字节数组生成对应的缓存对象;另外一种是调用静态方法allocateDirect,根据输入的数值分配指定大小的空缓存。字节缓存又是一种特殊的存储空间,由于它可能会被屡次读写,因此为了有效地控制读写操做,Java给它设计了下列五种概念:容量、当前限制量、当前位置、本次剩余空间、标记位置,分别说明以下:
一、容量(capacity):指的是字节缓存的整个长度。容量大小可经过缓存对象的capacity方法得到。
二、当前限制量(limit):指的是当前读写操做所能处理的最大空间大小。当前限制量可经过缓存对象的limit方法得到(不带输入参数),携带输入参数的limit方法用来设置当前限制量的数值。若是不设置当前限制量的大小,则limit数值默认为字节缓存的容量大小。
三、当前位置(position):指的是字节缓存当前操做的起始位置。当前位置可经过缓存对象的position方法得到(不带输入参数),携带输入参数的position方法用来设置当前位置的数值。字节缓存一开始的当前位置是0,每次进行读写操做位置以后,当前位置都会日后跟着挪动。
四、本次剩余空间(remaining):它的数值等于当前限制量减去当前位置(即limit-position)。本次剩余空间可经过缓存对象的remaining方法得到。
五、标记位置(mark):其概念相似缓存输入流的标记,一样是调用mark方法在当前位置作个标记,以便后续调用reset方法可以回到上次标记的位置。
举个例子,如今分配了一个容量大小为10的字节缓存,而且设置它的当前限制量为8,接着将当前位置移到第三个字节处(下标为2),那么该字节缓存的存储结构应当以下图所示。html
搞清楚了字节缓存的内部结构,再来看与字节缓存有关的数据流向。字节缓存与磁盘文件之间经过文件通道FileChannel交互,与内存字符串之间经过字节数组byte[]交互,因而内存中的一个字符串想要与磁盘上的某个文件内容相互转换的话,就存在如下两种数据流转过程:
一、把字符串写入文件,此时数据流向为:字符串String→字节数组byte[]→字节缓存ByteBuffer→指定路径的文件。
二、把文件内容读到字符串,此时数据流向为:指定路径的文件→字节缓存ByteBuffer→字节数组byte[]→字符串String。
其中与字节缓存有关的读写操做又可拆分为下列四种方法调用:
一、字节数组byte[]→字节缓存ByteBuffer,该操做除了调用ByteBuffer的静态方法wrap以外,还能经过缓存对象的put方法往字节缓存写入字节数组。
二、字节缓存ByteBuffer→指定路径的文件,该操做须要调用通道对象的write方法,往磁盘文件写入字节缓存中的数据。
三、指定路径的文件→字节缓存ByteBuffer,该操做须要调用通道对象的read方法,把磁盘文件中的数据读到字节缓存。
四、字节缓存ByteBuffer→字节数组byte[],该操做须要经过缓存对象的get方法,把字节缓存中的数据取到字节数组。
详细的数据流转过程可见下图,其中动做①和动做②实现了将字符串写入文件的功能,动做③和动做④实现了将文件内容读到字符串的功能。数组
注意到上图的动做①与动做③都是把数据输入给字节缓存,所以这两个动做可视为对字节缓存的写操做。而动做②与动做④都是从字节缓存中取出数据,所以这两个动做可视为对字节缓存的读操做。那么反复读写可能产生不一样的处理需求,好比把当前位置挪回字节缓存的开头,接下来是要写入数据仍是读出数据,为此ByteBuffer又提供了下列四个方法:
clear:缓冲区数据写入通道以后,若是还想把新数据写入缓冲区,就要先调用clear方法清空它。
compact:只清除已经读过的数据,剩余的未读数据会移到缓冲区开头,新增的数据将加到未读数据后面。
flip:把缓冲区从写模式切换到读模式。从缓冲区读取数据以前,必须先调用flip方法。
rewind:让缓冲区的指针回到开头,以便从新再来一遍。
上面的四个方法在部分功能上互有异同点,为了更好地梳理它们之间的区别,下面整理了一个表格,说明每一个方法在调用以后将会引发哪些参数的变化。
position limit mark
clear 0 容量大小 -1
compact 0 容量大小 -1
flip 0 上次的当前位置 -1
rewind 0 保持不变 -1缓存
就具体的代码逻辑而言,通常在写入字节缓存以前(上图的动做①与动做③),须要先调用compact方法;在读取字节缓存以前(上图的动做②与动做④),须要先调用flip方法。固然若是是建立字节缓存后的第一次操做,就没必要调用compact方法或者flip方法,由于在一开始字节缓存的当前位置都是指向0,无需再将当前位置挪回缓存开头了。回头看上一篇文章末尾经过文件通道读取文件的代码片断:设计
int size = (int) channel.size(); // 获取文件通道的大小(即文件长度) // 分配指定大小的字节缓存 ByteBuffer buffer = ByteBuffer.allocateDirect(size); channel.read(buffer); // 把文件通道中的数据读到字节缓存 buffer.flip(); // 把缓冲区从写模式切换到读模式。从缓冲区读取数据以前,必须先调用flip方法 byte[] bytes = new byte[size]; // 建立与文件大小相同长度的字节数组 buffer.get(bytes); // 把字节缓存中的数据取到字节数组
根据前面的文字介绍,可以很好地解释以上代码的方法调用次序。因为通道对象的read方法是建立字节缓存以后的首个读写操做,所以无需先调用compact方法;而缓存对象的get方法不是首个读写操做,就必须在get以前先调用flip方法了。指针
更多Java技术文章参见《Java开发笔记(序)章节目录》htm