Java NIO Buffers用于和NIO Channel交互。 咱们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels.html
Buffer本质上就是一块内存区,能够用来写入数据,并在稍后读取出来。这块内存被NIO Buffer包裹起来,对外提供一系列的读写方便开发的接口。java
在Java NIO中使用的核心缓冲区以下(覆盖了经过I/O发送的基本数据类型:byte, char、short, int, long, float, double ,long):程序员
当写入数据到buffer中时,buffer会记录已经写入的数据大小。当须要读数据时,经过 flip() 方法把buffer从写模式调整为读模式;在读模式下,能够读取全部已经写入的数据。面试
当读取完数据后,须要清空buffer,以知足后续写入操做。清空buffer有两种方式:调用 clear() 或 compact() 方法。clear会清空整个buffer,compact则只清空已读取的数据,未被读取的数据会被移动到buffer的开始位置,写入位置则近跟着未读数据以后。api
Buffer缓冲区实质上就是一块内存,用于写入数据,也供后续再次读取数据。这块内存被NIO Buffer管理,并提供一系列的方法用于更简单的操做这块内存。数组
一个Buffer有三个属性是必须掌握的,分别是:缓存
position和limit的具体含义取决于当前buffer的模式。capacity在两种模式下都表示容量。微信
下面有张示例图,描诉了读写模式下position和limit的含义:oracle
容量(Capacity)
做为一块内存,buffer有一个固定的大小,叫作capacit(容量)。也就是最多只能写入容量值得字节,整形等数据。一旦buffer写满了就须要清空已读数据以便下次继续写入新的数据。yii
位置(Position)
当写入数据到Buffer的时候须要从一个肯定的位置开始,默认初始化时这个位置position为0,一旦写入了数据好比一个字节,整形数据,那么position的值就会指向数据以后的一个单元,position最大能够到capacity-1.
当从Buffer读取数据时,也须要从一个肯定的位置开始。buffer从写入模式变为读取模式时,position会归零,每次读取后,position向后移动。
上限(Limit)
在写模式,limit的含义是咱们所能写入的最大数据量,它等同于buffer的容量。
一旦切换到读模式,limit则表明咱们所能读取的最大数据量,他的值等同于写模式下position的位置。换句话说,您能够读取与写入数量相同的字节数(限制设置为写入的字节数,由位置标记)。
方法 | 介绍 |
---|---|
abstract Object array() | 返回支持此缓冲区的数组 (可选操做) |
abstract int arrayOffset() | 返回该缓冲区的缓冲区的第一个元素的背衬数组中的偏移量 (可选操做) |
int capacity() | 返回此缓冲区的容量 |
Buffer clear() | 清除此缓存区。将position = 0;limit = capacity;mark = -1; |
Buffer flip() | flip()方法能够吧Buffer从写模式切换到读模式。调用flip方法会把position归零,并设置limit为以前的position的值。 也就是说,如今position表明的是读取位置,limit标示的是已写入的数据位置。 |
abstract boolean hasArray() | 告诉这个缓冲区是否由可访问的数组支持 |
boolean hasRemaining() | return position < limit,返回是否还有未读内容 |
abstract boolean isDirect() | 判断个缓冲区是否为 direct |
abstract boolean isReadOnly() | 判断告知这个缓冲区是不是只读的 |
int limit() | 返回此缓冲区的限制 |
Buffer position(int newPosition) | 设置这个缓冲区的位置 |
int remaining() | return limit - position; 返回limit和position之间相对位置差 |
Buffer rewind() | 把position设为0,mark设为-1,不改变limit的值 |
Buffer mark() | 将此缓冲区的标记设置在其位置 |
分配缓冲区(Allocating a Buffer)
为了得到缓冲区对象,咱们必须首先分配一个缓冲区。在每一个Buffer类中,allocate()方法用于分配缓冲区。
下面来看看ByteBuffer分配容量为28字节的例子:
ByteBuffer buf = ByteBuffer.allocate(28);
下面来看看另外一个示例:CharBuffer分配空间大小为2048个字符。
CharBuffer buf = CharBuffer.allocate(2048);
写入数据到缓冲区(Writing Data to a Buffer)
写数据到Buffer有两种方法:
下面是一个实例,演示从Channel写数据到Buffer:
int bytesRead = inChannel.read(buf); //read into buffer.
经过put写数据:
buf.put(127);
put方法有不少不一样版本,对应不一样的写数据方法。例如把数据写到特定的位置,或者把一个字节数据写入buffer。看考JavaDoc文档能够查阅的更多数据。
翻转(flip())
flip()方法能够吧Buffer从写模式切换到读模式。调用flip方法会把position归零,并设置limit为以前的position的值。 也就是说,如今position表明的是读取位置,limit标示的是已写入的数据位置。
从Buffer读取数据(Reading Data from a Buffer)
从Buffer读数据也有两种方式。
读取数据到channel的例子:
int bytesWritten = inChannel.write(buf);
调用get读取数据的例子:
byte aByte = buf.get();
get也有诸多版本,对应了不一样的读取方式。
rewind()
Buffer.rewind()方法将position置为0,这样咱们能够重复读取buffer中的数据。limit保持不变。
clear() and compact()
一旦咱们从buffer中读取完数据,须要复用buffer为下次写数据作准备。只须要调用clear()或compact()方法。
若是调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉咱们能够从哪里开始往Buffer里写数据。
若是Buffer还有一些数据没有读取完,调用clear就会致使这部分数据被“遗忘”,由于咱们没有标记这部分数据未读。
针对这种状况,若是须要保留未读数据,那么可使用compact。 所以 compact() 和 clear() 的区别就在于: 对未读数据的处理,是保留这部分数据仍是一块儿清空 。
mark()与reset()方法
经过调用Buffer.mark()方法,能够标记Buffer中的一个特定position。以后能够经过调用Buffer.reset()方法恢复到这个position。例如:
buffer.mark(); //call buffer.get() a couple of times, e.g. during parsing. buffer.reset(); //set position back to mark.
equals() and compareTo()
能够用eqauls和compareTo比较两个buffer
equals():
判断两个buffer相对,需知足:
从上面的三个条件能够看出,equals只比较buffer中的部份内容,并不会去比较每个元素。
compareTo():
compareTo也是比较buffer中的剩余元素,只不过这个方法适用于比较排序的:
这里以ByteBuffer为例子说明抽象类Buffer的实现类的一些常见方法的使用:
package channel; import java.nio.ByteBuffer; public class ByteBufferMethods { public static void main(String args[]){ //分配缓冲区(Allocating a Buffer) ByteBuffer buffer = ByteBuffer.allocate(33); System.out.println("-------------Test reset-------------"); //clear()方法,position将被设回0,limit被设置成 capacity的值 buffer.clear(); // 设置这个缓冲区的位置 buffer.position(5); //将此缓冲区的标记设置在其位置。没有buffer.mark();这句话会报错 buffer.mark(); buffer.position(10); System.out.println("before reset: " + buffer); //将此缓冲区的位置重置为先前标记的位置。(buffer.position(5)) buffer.reset(); System.out.println("after reset: " + buffer); System.out.println("-------------Test rewind-------------"); buffer.clear(); buffer.position(10); //返回此缓冲区的限制。 buffer.limit(15); System.out.println("before rewind: " + buffer); //把position设为0,mark设为-1,不改变limit的值 buffer.rewind(); System.out.println("before rewind: " + buffer); System.out.println("-------------Test compact-------------"); buffer.clear(); buffer.put("abcd".getBytes()); System.out.println("before compact: " + buffer); System.out.println(new String(buffer.array())); //limit = position;position = 0;mark = -1; 翻转,也就是让flip以后的position到limit这块区域变成以前的0到position这块, //翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态 buffer.flip(); System.out.println("after flip: " + buffer); //get()方法:相对读,从position位置读取一个byte,并将position+1,为下次读写做准备 System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println("after three gets: " + buffer); System.out.println("\t" + new String(buffer.array())); //把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。 // 若是先将positon设置到limit,再compact,那么至关于clear() buffer.compact(); System.out.println("after compact: " + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("-------------Test get-------------"); buffer = ByteBuffer.allocate(32); buffer.put((byte) 'a').put((byte) 'b').put((byte) 'c').put((byte) 'd') .put((byte) 'e').put((byte) 'f'); System.out.println("before flip(): " + buffer); // 转换为读取模式 buffer.flip(); System.out.println("before get(): " + buffer); System.out.println((char) buffer.get()); System.out.println("after get(): " + buffer); // get(index)不影响position的值 System.out.println((char) buffer.get(2)); System.out.println("after get(index): " + buffer); byte[] dst = new byte[10]; buffer.get(dst, 0, 2); System.out.println("after get(dst, 0, 2): " + buffer); System.out.println("\t dst:" + new String(dst)); System.out.println("buffer now is: " + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("-------------Test put-------------"); ByteBuffer bb = ByteBuffer.allocate(32); System.out.println("before put(byte): " + bb); System.out.println("after put(byte): " + bb.put((byte) 'z')); System.out.println("\t" + bb.put(2, (byte) 'c')); // put(2,(byte) 'c')不改变position的位置 System.out.println("after put(2,(byte) 'c'): " + bb); System.out.println("\t" + new String(bb.array())); // 这里的buffer是 abcdef[pos=3 lim=6 cap=32] bb.put(buffer); System.out.println("after put(buffer): " + bb); System.out.println("\t" + new String(bb.array())); } }
欢迎关注个人微信公众号:"Java面试通关手册"(一个有温度的微信公众号,期待与你共同进步~~~坚持原创,分享美文,分享各类Java学习资源):