缓冲区分类
NIO中的buffer用于和通道交互,数据是从通道读入缓冲区,从缓冲区中写入通道的。Buffer就像一个数组,可以保存多个类型相同的数据。每种基本数据类型都有对应的Buffer类:
缓冲区的属性
1、capacity容量):buffer本质是一个数组,在初始化时有固定的大小,这个值就是容量。容量不可改变,一旦缓冲区满了,需要将其清空才能将继续进行读写操作。
2、position位置):表示当前的位置,初始化时为0,当一个基本数据类型的数据写入buffer时,position会向前移动到下一个可插入数据的Buffer单元。position最大值可以是 capacity-1。
3、limit(限制):在缓冲区写模式下,limit表示你最多能往Buffer里写多少数据,大小等于capacity;在缓冲区读模式下,limit表示能从缓冲区内读取到多少数据,因此,当切 换Buffer到读模式时,limit会被设置成写模式下的position值。
一、使用NIO进行文件内容的复制:
public class BufferTest { public static void mainString[] args) throws Exception { FileInputStream fis = new FileInputStream"d:/in.txt"); FileChannel channel = fis.getChannel); FileOutputStream fos = new FileOutputStream"d:/out.txt"); FileChannel channel1 = fos.getChannel); //初始化缓冲区 ByteBuffer buffer = ByteBuffer.allocate20); System.out.println"通道文件的大小:" + channel.size)); System.out.println"缓冲区初始化时当前位置:" + buffer.position)); System.out.println"缓冲区初始化时可写的限制:" + buffer.limit)); System.out.println"---------循环开始-----"); //判断通道内数据是否读取完成 while-1 != channel.readbuffer)) { System.out.println"缓冲区写模式下当前位置:" + buffer.position)); System.out.println"缓冲区写模式下的限制:" + buffer.limit)); //将缓冲区从写模式切换到读模式 buffer.flip); System.out.println"缓冲区读模式下当前位置:" + buffer.position)); System.out.println"缓冲区读模式下的限制:" + buffer.limit)); //判断缓冲区内是否还有数据可读取 whilebuffer.hasRemaining)) { channel1.writebuffer); } buffer.clear); } channel.close); channel1.close); fis.close); } }
执行结果:
通道文件的大小:36 缓冲区初始化时当前位置:0 缓冲区初始化时可写的限制:20 ---------循环开始----- 缓冲区写模式下当前位置:20 缓冲区写模式下的限制:20 缓冲区读模式下当前位置:0 缓冲区读模式下的限制:20 缓冲区写模式下当前位置:16 缓冲区写模式下的限制:20 缓冲区读模式下当前位置:0 缓冲区读模式下的限制:16
1、文件的大小为36个字节,缓冲区初始化的大小为20个字节,程序中进行了两次读取操作,才完成了文件内容的复制。
2、可以看到,在缓冲区写模式下,limit的大小始终等于capacity;而在读模式下,limit等于模式切换前position的大小。
二、Buffer的分配
Buffer对象的获取需要进行分配,每种类型的Buffer对象都有一个allocate方法。我们以程序中的ByteBuffer对象为例:
ByteBuffer buffer = ByteBuffer.allocate20);
我们去跟踪下源码:
1 public static ByteBuffer allocateint capacity) { 2 if capacity < 0) 3 throw new IllegalArgumentException); 4 return new HeapByteBuffercapacity, capacity); 5 }
HeapByteBufferint cap, int lim) { // package-private super-1, 0, lim, cap, new byte[cap], 0);
//在这里已经创建一个以cap为大小的字节数组(new byte[cap])
ByteBufferint mark, int pos, int lim, int cap, // package-private byte[] hb, int offset) { supermark, pos, lim, cap); this.hb = hb; this.offset = offset; }
Bufferint mark, int pos, int lim, int cap) { // package-private if cap < 0) throw new IllegalArgumentException); this.capacity = cap; limitlim); positionpos); if mark >= 0) { if mark > pos) throw new IllegalArgumentException); this.mark = mark; } }
//数组的创建在ByteBuffer类里面已经创建,在父类Buffer里,初始化容量、限制、位置等一些公共属性。
三、Buffer模式的切换
buffer.flip)该方法是用于将缓冲区从写模式切换到读模式,这是一种固定写法,该方法的源码如下:
public final Buffer flip) { limit = position; position = 0; mark = -1; return this; }
调用flip)方法会将position设回0,并将limit设置成之前position的值。
四、remaind方法
public final Buffer rewind) { position = 0; mark = -1; return this; }
将position的位置设置为0,表示可以重新读取Buffer中的所有数据,limit保持不变。
五、clear方法
1 public final Buffer clear) { 2 position = 0; 3 limit = capacity; 4 mark = -1; 5 return this;
1、一旦完成对buffer中数据的读取,需要让buffer做好再次被写入的准备,这时候可以调用clear方法来完成。
2、clear方法将position设置为0,limit设置为容量的值,也就意味着buffer被清空了,但是这个清空的概念是写入数据可以从缓冲区的指定位置开始,但buffer里面的数据并没有 删除。
3、如果buffer里面还有数据没有被读取,这个时候调用clear方法会导致那些数据被“遗忘”,因为没有标记告诉你哪些是读取过哪些没有被读取。
六、向buffer中写入数据
1、通过channel写入;
2、通过buffer的put方法写入:
buffer.put"channel".getBytes));
七、从buffer中读取数据
1、通过channel读取;
2、通过buffer的get方法读取:
byte b = buffer.get);