我们知道NIO的三大核心是buffer,channel和selector,本文开始详细介绍下buffer
缓冲区Buffer
1.缓冲区介绍
一个Buffer对象是固定数量的数据的容器。其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。缓冲区可以写满和释放。对于每个非布尔原始数据类型都有一个缓冲区类。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。
缓冲区的工作与通道紧密联系。通道是 I/O 传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。
下图是Buffer的类层次图。在顶部是通用Buffer类,Buffer 定义所有缓冲区类型共有的操作,无论是它们所包含的数据类型还是可能具有的特定行为。这一共同点将会成为我们的出发点。
2.缓冲区操作
概念上,缓冲区是包在一个对象内的基本数据元素数组。Buffer 类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。Buffer 类以及它专有的子类定义了一个用于处理数据缓冲区的 API。
2.1 创建缓冲区
新的缓冲区是由分配或包装操作创建的.
分配方式:
// 创建一个ByteBuffer,容量为10 ByteBuffer byteBuffer = ByteBuffer.allocate(10);
包装方式:
byte[] array = new byte[10]; ByteBuffer.wrap(array);
2.2 属性
所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。
四个属性之前总是遵循以下关系:
mark <= position <= limit <= capacity
举例:
// 创建一个ByteBuffer,容量为10 ByteBuffer byteBuffer = ByteBuffer.allocate(10);
位置被设为0,而且容量和上界被设为10,刚好经过缓冲区能够容纳的最后一个字节。标记最初未定义。容量是固定的,但另外的三个属性可以在使用缓冲区时改变
2.3 缓冲区API介绍
接下来我们先看下Buffer中提供的方法
package java.nio; public abstract class Buffer { public final int capacity( ) public final int position( ) public final Buffer position (int newPosition) public final int limit( ) public final Buffer limit (int newLimit) public final Buffer mark( ) public final Buffer reset( ) public final Buffer clear( ) public final Buffer flip( ) public final Buffer rewind( ) public final int remaining( ) public final boolean hasRemaining( ) public abstract boolean isReadOnly( ); }
put方法
'存取’也就将数据保存到缓冲区中及从缓冲区中取出数据,在Buffer类中并没有提供get和put方法,这两个方法在具体的Buffer子类中有提供,比如ByteBuffer.如下
public abstract class ByteBuffer extends Buffer implements Comparable { // This is a partial API listing public abstract byte get( ); public abstract byte get (int index); public abstract ByteBuffer put (byte b); public abstract ByteBuffer put (int index, byte b); }
保存数据到缓冲区
public static void main(String[] args) { // 创建一个ByteBuffer,容量为10 ByteBuffer byteBuffer = ByteBuffer.allocate(10); // 看一下初始时4个核心变量的值 System.out.println("初始时-->limit--->" + byteBuffer.limit()); System.out.println("初始时-->position--->" + byteBuffer.position()); System.out.println("初始时-->capacity--->" + byteBuffer.capacity()); System.out.println("初始时-->mark--->" + byteBuffer.mark()); System.out.println("--------------------------------------"); // 添加一些数据到缓冲区中 String s = "bobo"; byteBuffer.put(s.getBytes()); // 看一下初始时4个核心变量的值 System.out.println("put完之后-->limit--->" + byteBuffer.limit()); System.out.println("put完之后-->position--->" + byteBuffer.position()); System.out.println("put完之后-->capacity--->" + byteBuffer.capacity()); System.out.println("put完之后-->mark--->" + byteBuffer.mark()); }
输出:
初始时-->limit--->10 初始时-->position--->0 初始时-->capacity--->10 初始时-->mark--->java.nio.HeapByteBuffer[pos=0 lim=10 cap=10] -------------------------------------- put完之后-->limit--->10 put完之后-->position--->4 put完之后-->capacity--->10 put完之后-->mark--->java.nio.HeapByteBuffer[pos=4 lim=10 cap=10]
flip方法
现在我想要从缓存区拿数据,怎么拿呢?NIO给了我们一个flip()方法。这个方法可以改动position和limit的位置!
byteBuffer.flip(); System.out.println("flip完之后-->limit--->" + byteBuffer.limit()); System.out.println("flip完之后-->position--->" + byteBuffer.position()); System.out.println("flip完之后-->capacity--->" + byteBuffer.capacity()); System.out.println("flip完之后-->mark--->" + byteBuffer.mark());
flip完之后-->limit--->4 flip完之后-->position--->0 flip完之后-->capacity--->10 flip完之后-->mark--->java.nio.HeapByteBuffer[pos=0 lim=4 cap=10]
一般我们称filp()为“切换成读模式”
get方法
get方法读取信息position也会对应的移动!
// 一个字节一个字节的读取 System.out.println((char)byteBuffer.get()); System.out.println((char)byteBuffer.get()); System.out.println((char)byteBuffer.get()); System.out.println((char)byteBuffer.get()); System.out.println("get完之后-->mark--->" + byteBuffer.mark()); // get方法 读取了多少个字节,position就会移动多少位,相应再次重新读取需要flip byteBuffer.flip(); byte[] b = new byte[byteBuffer.limit()]; // 批量读取数据 byteBuffer.get(b); System.out.println(new String(b,0,b.length)); System.out.println("get完之后-->mark--->" + byteBuffer.mark());
输出
b o b o get完之后-->mark--->java.nio.HeapByteBuffer[pos=4 lim=4 cap=10] bobo
clear方法
数据操作完成后我们还想要继续写入数据,这时我们可以使用clear方法来’清空’缓冲区。数据没有真正被清空,只是被遗忘掉了
hasRemaining()
检查position和limit之间是否还有元素。判断是否还有剩余元素
// 一个字节一个字节的读取 System.out.println((char)byteBuffer.get()); System.out.println("hasRemaining:"+byteBuffer.hasRemaining()); System.out.println((char)byteBuffer.get()); System.out.println("hasRemaining:"+byteBuffer.hasRemaining()); System.out.println((char)byteBuffer.get()); System.out.println("hasRemaining:"+byteBuffer.hasRemaining()); System.out.println((char)byteBuffer.get()); System.out.println("hasRemaining:"+byteBuffer.hasRemaining());
输出
b hasRemaining:true o hasRemaining:true b hasRemaining:true o hasRemaining:false
rewind方法
读完一遍数据后,我们还想再读取一遍,此时可以考虑rewind方法
注意他和flip方法的区别
通过源码分析会更加清晰些~