NIO(JDK1.4)--缓冲区

来源:互联网 发布:淘宝网1 编辑:程序博客网 时间:2024/06/05 14:12
基础
1、缓冲区API:
让我们来看一下可以如何使用一个缓冲区。以下是Buffer类的方法签名:
package java.nio;  
public abstract  class  Buffer {  
        public final  int  capacity(  )  
        public final  int  position(  )  
        public final  Buffer position (int newPositio )
        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(  );  
}
关于这个API 有一点要注意的是,像 clear()这类函数,您通常应当返回 void,而不 是 Buffer 引用。这些函数将引用返回到它们在(this)上被引用的对象。这是一个允许级 联调用的类设计方法。级联调用允许这种类型的代码:
buffer.mark();  
buffer.position(5); 
buffer.reset(); 
被简写为:
buffer.mark().position(5).reset();
2、翻转:
假如我们想把一个缓冲区传递给一个通 道,以使内容能被全部写出。但如果通道现在在缓冲区上执行 get(),那么它将从position指向的位置开始取出数据,如图1所示,而position在调用put()写入数据时会往后移动的,所以,在写出数据前,应先把position移到缓冲区的开头,但是它是怎样知道何时到达我们所插入数据末端的呢?这就是上界属性被引入的目的。上界属性指明了缓冲区有效内容的末端。我们需要将上界属性设置为当前位置,然后将位 置重置为0。我们可以人工用下面的代码实现:
buffer.limit(buffer.position()).position(0);
但这种从填充到释放状态的缓冲区翻转是 API 设计者预先设计好的,他们为我们提供了 一个非常便利的函数:
buffer.flip(); 
flip()函数将一个能够继续添加数据元素的填充状态的缓冲区翻转成一个准备读出元素 的释放状态。在翻转之后,图1的缓冲区状态会变成图2的缓冲区状态。
图1:
图2:翻转后的缓冲区
rewind()函数与flip()相似,但 rewind()不影响上界属性。它只是将位置值设回0。您可以使用rewind()后退,重读已经被翻转的缓冲区中的数据。
如果将缓冲区翻转两次会怎样呢?会把缓冲区大小变为0。比如对图2执行flip(),则 把上界设为位置的值,并把位置设为0。上界和位置都变成 0。尝试对缓冲区上位置和上界都为0的get()操作会导致BufferUnderflowException异常。而put()则会导致BufferOverflowException异常。
3、读取
如果您接收到一个在别处被填满的缓冲区,您可能需要在检索内容之前将其翻转。例如,如果一个通道的 read()操作完成,而您想要查看被通道放入缓冲区内的数据,那么您需要在调用 get()之前翻转缓冲区。通道对象在缓冲区上调用 put()增加数据;put和read可以随意混合使用。
布尔函数hasRemaining()返回是否已达到缓冲区的上界,如:
for (int i = 0; buffer.hasRemaining(), i++) {  
        myByteArray [i] = buffer.get();  
}
remaining()函数将告知您从当前位置到上界还剩余的元素数目。您也可以 通过下面的循环来读取缓冲区:
int count = buffer.remaining();  
for (int i = 0; i < count, i++) {
    myByteArray [i] = buffer.get();
}
clear()函数将缓冲区重置 为空状态。它并不改变缓冲区中的任何数据元素,而是仅仅将上界设为容量的值,并把位置设回0。然后我们可以重新往缓冲区写入数据,不要担心写入的数据过短而导致原来的数据没有完全被覆盖,因为在读之前,需要调用flip()翻转,翻转后,上界指向有效数据的下一个位置,然后再调用get()取出,这保证了上界之后的数据不会被读出。
例子:
package com;
import java.nio.CharBuffer;
public class test{
    public static void main(String [] argv)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.print (buffer.get());
        }
        System.out.println ("");
    }
    private  static  boolean  fillBuffer (CharBuffer buffer){
        if  (index >= strings.length) {
            return  false;
        }
        String str = strings [index++];
        for  (int  i = 0; i < str.length(); i++) {
            buffer.put (str.charAt (i));
        }
        return  true;
    }
    private  static  int  index = 0;
    private  static  String [] strings = {
        "A random string value",
        "The product of an infinite number of monkeys",
        "Hey hey we're the Monkees",
        "Opening act for the Monkees: Jimi Hendrix",
        "'Scuse me while I kiss this fly",
        "Help Me! Help Me!"
    };
}
4、压缩
有时,您可能只想从缓冲区中释放一部分数据,而不是全部,然后继续接着填充。为了实现这 一点,未读的数据元素需要向前移至头部,当前位置position要移到下一个写入数据的位置。可以重复调用get、put方法实现(get、put方法分别有get()、get(int index)、put()、put(int index),当然还有其它参数列表类型,可以指定读写的位置),但这样做会效率低下,而API对此提供了一个compact()函数。这一缓冲区工具在复制数据时 要比使用get()put()函数高效得多。所以当需要时,请使用compact(),下图是使用compact前后的缓冲区状态:

compact前:

708297.png

compact后:
800516.png
5、标记
缓冲区的标记在mark()函数被调用之前是未定义的,调用时标记被设为当前位置的值,标记的作用是:当需要的时候,可以用reset()把position设置为之前标记的位置,如果标记还没定义,调用reset()会报异常。调用 rewind()、 clear() 以及flip()函数时会导致标记被抛弃,调用limit()或position()的带参版本函数时,如果参数比当前标记小,则也会导致标记被抛弃。

如图1,调用buffer.position(2).mark().position(4);后得到图2

图1:

376820.png

图2:

156049.png

图2基础上调用reset(),会得到图3
图3:
600559.png

java.nio.ByteBuffer中flip、rewind、clear方法的区别

对缓冲区的读写操作首先要知道缓冲区的下限、上限和当前位置。下面这些变量的值对Buffer类中的某些操作有着至关重要的作用:
  1. limit:所有对Buffer读写操作都会以limit变量的值作为上限。
  2. position:代表对缓冲区进行读写时,当前游标的位置。
  3. capacity:代表缓冲区的最大容量(一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的)。
flip、rewind、clear这三个方法便是用来设置这些值的。

clear方法(一般是在重新写缓冲区之前调用)

public final Buffer clear()
{
    position = 0; //重置当前读写位置
    limit = capacity; 
    mark = -1;  //取消标记
    return this;
}


clear方法将缓冲区清空,所谓的清空不是指清空缓冲区数据,仅是改变position等属性的值,一般是在重新写缓冲区之前调用,调用clear之后如果马上读缓冲区数据,则可以读出之前填入的数据,但由于limit位置被设为capacity,所以,结尾是不可意料的。

flip方法(一般是在读缓冲区之前调用,常与compact方法一起使用)

public final Buffer flip()  {
 limit = position;
 position = 0;
 mark = -1;
 return this;
}
反转缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。 常与compact方法一起使用,因为可能之前已经被读了一部分数据。通常情况下,在准备从缓冲区中读取数据时调用flip方法。

rewind方法

1public final Buffer rewind()  {
2 position = 0;
3 mark = -1;
4 return this;
5}
如果limit本身已经指向数据的末端,则可以用该方法代替flip,如果position并不指向数据的末端,而limit指向数据末端则不可以用flip,应该用rewind。
以上三种方法均使用final修饰,java.nio.Buffer的所有子类均使用同一种flip、clear和rewind机制。

0 0
原创粉丝点击