本文来自于我的个人博客:
我们以Buffer类開始对java.nio包的浏览历程。这些类是java.nio的构造基础。
这个系列中,我们将尾随《java NIO》书籍一起深入研究缓冲区,了解各种不同的类型。并学会如何使用。
一个Buffer对象是固定数量的数据容器。其作用是一个存储器。或者分段运输区。在这里数据可被存储并在之后用于检索。
Buffer类的家谱:
一,缓冲区基础
1.缓冲区的属性:
容量(capacity):缓冲区可以容纳的数据元素的最大数量。这一容量是在缓冲区被创建时设置的,而且永远不能被改变
上界(limit): 缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
位置(position): 下一个要被读或写的元素的索引,位置会自己主动由对应的get()和put()函数更新。
标记(mark): 一个备忘位置,调用mark()来设定mark=position.调用reset()设定position=mark。
标记在设定前是没有定义的(undefied)。
这四个属性的关系例如以下:
0 <= mark <= position <= limit <= capacity
接下来,我们看看这些属性在实际应用中的一些样例:
创建一个新的ByteBuffer,则缓冲区的各个属性状态例如以下图所看到的:
位置被设置为0,并且容量和上界被设为10,刚好经过缓冲区可以容纳的最后一个字节。
标记最初没有定义,容量是固定的,但另外三个属性可以在使用缓冲区时改变。
2.缓冲区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 flip(); public final Buffer rewind(); public final int remaining(); public final boolean hasRemaining(); public abstract boolean isReadOnly();}
关于这个API。有一点要注意,像clear()这类方法,通常应当返回void,而不是Buffer引用。这些函数将引用返回到它们在(this)上被引用的对象。这是一个同意级联调用的类设计方法。
级联调用同意这样的类型的代码:
buffer.mark();
buffer.position(5);
buffer.reset();
被简写为:buffer.mark().position(5).reset();
3.存取
我们将代表"Hello"字符串的ASCII码加载一个名为buffer的ByteBuffer对象中,缓冲区的结果状态例如以下图所看到的:
buffer.put((byte)'H').put((byte)'e').put((byte)'1').put((byte)'1').put((byte)'o');
既然已经在buffer中存放了一些数据。如果我们想在不丢失位置的情况下进行一些更改该怎么办呢?put()的绝对方案能够达到这种目的。如果我们想将缓冲区中的内容从"Hello"的ASCII码更改为"Mellow"。我们能够这样实现:
buffer.put(0,(byte)'M').put((byte)'w');改动后的buffer例如以下图所看到的:
4.翻转
在我们写满了缓冲区之后,如今我们必须准备将其清空。
我们想把这个缓冲区传递给一个通道,以使内容能被所有写出,但假设通道如今在缓冲区上运行get(),那么它将从我们刚刚插入的游泳数据之外取出没有定义的数据,假设我们将位置设置为0,通道就会从正确位置開始获取。可是它使如何知道何时叨叨我们所插入数据末端的呢?这就是上界属性被引入的目的。buffer.limit(buffer.position()).position(0),可是这样的从填充到释放转台的缓冲区翻转是预先设计好的。设计者为我们提供了更便利的方法buffer.flip(),flip方法将一个可以继续加入数据元素的填充状态的缓冲区饭庄成一个准备读出元素的释放状态。在翻转之后,缓冲区的状态例如以下图所看到的:
rewind()方法与flip()方法相似,可是它会改变limit的值,仅仅是将position的值设置为0,这种方法能够用于回退已经度过的数据以便又一次读取已经读过的数据。
方法hasRemaining会告诉你,是否已经达到缓冲区的上界,即limit-position=0,下面是一种将数据元素从缓冲区释放到一个数组的方法:
for(int i = 0; buffer.hasRemaining();i++{
byteArray[i] = buffer.get();
}
另一种选择是使用remainig()方法。这种方法告诉你还剩余多少元素数目可被存入,能够依据下面方法释放缓冲:
int count = buffre.remaining();
for(int i = 0; i < count;i++) {
byteArray[i] = buffer.get();
}
5.以下是一个样例来填充以及释放缓冲区:
import java.nio.CharBuffer;public class BufferFillDrain{ private static int index = 0; private static String[] strings = { "hello word", "java is very beautiful", "python is very brief" }; public static void main(String[] args) throws Exception{ CharBuffer buffer = CharBuffer.allocate(100); while(fillBuffer(buffer)) { buffer.flip(); drainBuffer(buffer); buffer.clear(); } } private static void drainBuffer(CharBuffer buffer) { while(buffer.hasRemaining()) { System.out.println(buffer.get()); } } private static boolean fillBuffer(CharBuffer buffer) { if(index >= strings.length) { String string = strings[length]; for(int i = 0; i < strings.length(); i++) { buffer.put(string.charAt(i)); } } return true; }}
6.压缩
有时。我们可能仅仅想从缓冲区中释放一部分数据。而不是所有。然后又一次填充,为了实现这一点,未读的数据元素须要下移以使第一个元素索引为0。而API对此提供了一个compact()方法,在释放缓冲区曾经。可能有个缓冲区例如以下所看到的:
调用buffer.compact()方法之后。缓冲区的状态例如以下图所看到的:
能够看到,已经将释放的字节剔除。然后position的位置将会变为limit - originPosition。limit则被设置为limit=capacity。然后又又一次从position位置填充数据。
7.缓冲区的比較
两个被觉得是同样缓冲区的条件是从position位置開始到limit位置结束。这段字节之内的数据同样,则说明两个缓冲区同样,比較方法为buffer.compareTo(buffer),例如以下图所看到的两个缓冲区同样: