Java NIO 学习(二)--Buffer
来源:互联网 发布:缝纫机乐队 知乎 编辑:程序博客网 时间:2024/05/13 10:01
在第一节中,简单的描述了NIO三个核心的类:channel、buffer、selector;由于缓冲区作为操作的基本,而且底层channel接口没有过多细节(只有两个方法:isOpen、close)这里先讲解缓冲区的详细内容,后面再讲解具体类型的channel和selector;
一、buffer内部细节
在上一节的简单实例中有讲到,缓冲区的读模式和写模式,为什么还会有这样来区分呢?缓冲区可以认为是一个容器,可以看做是某个类型的数组,既然是容器、数组那就应该有容量大小、下标(index),不可能无限读或者写,读写时必须有指定的下标值;
缓存区的读写模式内部其实是使用三个状态变量来控制:
- capacity
- position
- limit
(一)、状态变量
- capacity
这个状态变量表示缓冲区的容量大小,这个值是固定不变的,指向缓冲区底层数组的最后一个位置;
2. position
在读模式下,该值表示下一个读取数据的位置,通过flip方法切换为读模式时,该值为指向缓存区第一个数据位置;在写模式下,该值表示下一个要写入数据的位置,通过clear方法切换为写模式时,该值指向缓存区第一个数据位置;
3. limit
这个状态变量表示缓存区可读/写的最大位置:在写模式下,该值等于capacity;在读模式下,该值等于切换为读模式时position的位置,表示可读数据位置;
(二)、读写模式改变方法
- flip方法
改方法必须要在读取缓冲区数据前调用;flip方法将limit变量的位置设置为当前position,将position设置为0,然后就可从缓冲区读取数据;
2. clear方法
该方法必须在向缓冲区写入数据前调用;clear方法将limit变量设置为和capacity一样,将position设置为0;
3. compact方法
该方法可以作为切换写模式调用,它并不像clear方法清空整个缓冲区,只是清除已经读取过的数据,没有被读取过的数据会移动到缓冲区前面:position位置设置为 limit-position,而limit还是设置为和capacity一样;
4. rewind方法
该方法将position变量从设置为0,limit变量保存不变,改方法可以用于读取数据后进行重新读取;
5. mark方法与reset方法
通常这个两个方法是一起配合使用的;mark方法用于标记当前缓冲区的position位置,reset方法用于恢复position为mark的位置;可以用于重复读取某段数据;如果调用reset方法是,缓冲区中没有一个mark,将会抛出InvalidMarkException异常;注意:调用clear、flip、compact、rewind方法会清除掉这个mark;
二、创建缓冲区
- 缓冲区分配与包装
在对通道进行读和写之前,必须先有一个缓冲区;创建缓冲区,通常通过静态方法allocate分配一个缓冲区:
ByteBuffer buffer = ByteBuffer.allocate(1024);
allocate方法分配一个指定大小的底层数组,并包装为一个缓存缓冲区对象;
另外还可以通过一个现有的数组直接包装为缓冲区:
byte array[] = new byte[1024];ByteBuffer buffer = ByteBufer.wrap(array);
注意:通过这种方式创建的缓冲区,因为有底层数组的实际引用,可以通过array数组直接修改数据;
2. 缓冲区分片
缓冲区可以使用slice方法创建一个子缓冲区,即是创建一个新的缓冲区,但是共享原缓冲区的一部分数据,使用下面实例代码说明slice方法:
public static void main(String[] args) { //创建一个容量为10的字节缓冲区 ByteBuffer buffer = ByteBuffer.allocate(10); //设置缓冲区中的数据 for (int i=0; i<buffer.capacity(); ++i) { buffer.put(i, (byte) i); } //手动设置状态变量,创建一个包含原缓冲区index3-6的子缓冲区(分片) buffer.position(3); buffer.limit(7); ByteBuffer slice = buffer.slice(); //对新创建的子缓冲区中每个数据乘10操作 for (int i=0; i<slice.capacity(); ++i) { byte b = slice.get(i); b*=10; slice.put(i, b); } //重设状态变量 buffer.position(0); buffer.limit(buffer.capacity()); while (buffer.hasRemaining()) { System.out.println(buffer.get()); } }
输出的结果为:
01230405060789
从实例代码可以看出,slice方法从position和limit变量间创建分片,并且分片数据和原缓冲区是共享的;
分片对于方法调用是有很大作用的,对调用的方法,如果只想方法处理其中一部分数据,可以通过slice传递一个子缓冲区作为参数;
3. 只读缓冲区
只读缓冲区只能用于读取数据,不能写入数据;通过缓冲区的asReadOnlyBuffer创建一个新的缓冲区,它与原缓冲区共享数据,但是是只读的;
只读缓冲区主要用于数据的保护,比如:调用一个方法是,可能需要保证缓冲区的数据不被修改,那就可以创建一个只读缓冲区作为参数;
三、分散与聚集(Scatter/Gather)
分散和聚集 I/O 是使用多个(数组)而不是单个缓冲区进行数据读/写;
分散(Scatter)从通道中读取数据时写入多个缓冲区中,通道将数据“分散”到多个缓冲区中;
聚集(Gather)写入通道时将多个缓冲区的数据写入同一个通道,通道将多个缓冲区数据“聚集”到一起;
支持分散读取和聚集写入的通道分别需要继承实现ScatteringByteChannel和GatheringByteChannel接口:
public interface ScatteringByteChannel extends ReadableByteChannel{ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException; public long read(ByteBuffer[] dsts) throws IOException;}
read方法像普通读取方法一样,只不多参数是缓冲区数组;
读取时,通道依次填充每个缓冲区,填充满一个后,开始填充下一个;
public interface GatheringByteChannel extends WritableByteChannel{ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException; public long write(ByteBuffer[] srcs) throws IOException;}
同样,write方法参数是一个缓冲区数组;
写入时,依次写入每个缓冲区的数据;
分散/聚集方便实现复杂且有固定格式数据的传输,比如网络通信的消息传输,可以统计为固定长度的消息头、消息体,就可通过两个固定长度的缓冲区进行分散/聚集的传输;
这节详细描述了缓冲区一些内部细节和方法,以及创建、分片、分散/聚集等,主要使用ByteBuffer描述,因为ByteBuffer更加通用;其他基本类型的buffer不做详细描述,因为与ByteBuffer类似;有其他特殊的buffer,如直接缓冲区等后续章节继续学习讲解;
- Java NIO 学习(二)--Buffer
- java学习-NIO(二)Buffer
- NIO学习(二) buffer
- Java NIO (二)--Buffer
- 《Java NIO》学习笔记二 缓冲区(Buffer)
- Java NIO学习总结二(Buffer缓冲区)
- JAVA NIO Buffer的学习
- Java NIO系列教程(二) Buffer
- Java NIO学习——Buffer学习
- Java NIO笔记(二):NIO Buffer(缓冲区)之基础
- java-NIO 学习(chapter3-Buffer)
- java nio 学习之 Buffer 类
- java nio编程学习笔记(3)--buffer
- Java NIO学习二
- Java NIO学习笔记二(Buffer的flip()方法详解)
- java nio之Buffer
- java.nio.Buffer分析
- java.nio.Buffer分析
- 待机切换场景
- solve.py 调试
- 6.5 单片机数码管显示消隐
- 新元素之video,audio,meter,datalist,keygen,output
- VC OPT:REF
- Java NIO 学习(二)--Buffer
- iOS 获取图片的主体颜色(主色调)
- 6.6 单片机中断系统
- LeetCode Self Crossing(判断是否相交)
- 6.7 单片机中断的优先级
- Android学习笔记--GMS认证中常见的fail项及解决方法
- cdh卸载
- 2016C语言期末考试 试题以及答案1
- 提升Linux 终端用户工作效率的几个小技巧