java IO相关API探索之Buffer类

来源:互联网 发布:asio mac驱动 编辑:程序博客网 时间:2024/06/09 15:54

FileChannel类中直接通信的就是Buffer,Buffer是通道和文件之间的桥梁.大概数据交互是这样的,FileChannel可以写数据到buffer中,同时另外一个输出FileChannel也可以从buffer中读取数据。


buffer中有两种模式:读模式和写模式,读模式会从position位置开始读取到limit字节;写模式同样也会从position写到limit,但是写模式中一般limit=capacity(buffer的大小)

Buffer 中有四个变量来标示一些位置:

 // Invariants: mark <= position <= limit <= capacity

    private int mark = -1;

    private int position = 0;

   private intlimit;

   private intcapacity;

这里面有一个恒不等式,就是上面指出的:mark<=position<=limit<=capacity

 // Used only by direct buffers

   // NOTE: hoisted here for speed in JNI GetDirectBufferAddress

   longaddress;

有一个存储关于地址的变量.看解释好像是为了提高JNI的速度。

下面是Buffer得构造函数,声明为default类型的,只在包里面可见

Buffer(intmark, int pos,int lim, intcap) {       // package-private

        if (cap < 0)

           throw new IllegalArgumentException("Negative capacity: " +cap);

        this.capacity =cap;

        limit(lim);

        position(pos);

        if (mark >= 0) {

           if (mark >pos)

               throw new IllegalArgumentException("mark > position: ("

                                                   +mark + " > " + pos +")");

           this.mark =mark;

        }

    }

下面是不允许重写的capacity()和position()方法:

publicfinal int capacity() {

        return capacity;

   }

 publicfinal int position() {

        return position;

    }

设置position的值,以便能够满足重复读取通道中已经读过的内容需求,如果设置position时,使得mark的值>position,那么mark的值将会置为-1;下面是这个方法的实现:

  publicfinal Buffer position(intnewPosition) {

        if ((newPosition >limit) || (newPosition < 0))

           throw new IllegalArgumentException();

        position = newPosition;

        if (mark > position) mark = -1;

        return this;

    }

设置limit的值,如果limit>capacity那么抛出不合法参数异常,否则设置limit = newLimit,如果当前buffer的position的值大于limit,那么设置position=limit,要不然在写数据的时候从position读到limit会永远都写不进去了,可能会出现数据不一致或者读写数据出错的情况,这点要注意;

publicfinal Buffer limit(intnewLimit) {

        if ((newLimit >capacity) || (newLimit < 0))

           throw new IllegalArgumentException();

        limit = newLimit;

       if (position >limit)position = limit;

        if (mark > limit) mark = -1;

        return this;

    }

如果buffer调用了mark()方法,那么会使得mark的值等于position,每次读数据或者写数据的时候position会递增,设置,调用mark方法,应该可以从下次再重复读取内容了。但是在buffer中没有找到reset方法,所以目前这个作用也只是个猜测(卧槽,这不坑爹么.刚才猜测的时候全局搜索reset根本找不到.竟然在mark方法的下一个方法就是reset,这就显而易见了,position都回到了mark地方了,肯定就可以重复读取了.)

publicfinal Buffer reset() {

        int m = mark;

        if (m < 0)

           throw new InvalidMarkException();

        position = m;

        return this;

    }

调用clear方法会使得数据全部回归原来的状态,这样在写入buffer的时候,就又可以从position=0开始写入了,如果position = limit的话,就没有办法写入到buffer中了。

publicfinal Buffer clear() {

        position = 0;

       limit = capacity;

        mark = -1;

        return this;

    }

但是buffer中的内容根本不会清除,如果在clear()之后又去读里面的内容很可能就会造成问题,刚才试了试,会造成死循环:代码如下:

//读数据到buffer中,比如position=0,limit=capacity=50,输入通道中有15个字节

while(channelIn.read(buffer) != -1) {

//这样的话,buffer中的值应该是position=15,limit=capacity=50,mark=-1;调用flip()方法之后

buffer.flip();

//会使得limit= position =15,position = 0;capacity = 50,mark = -1;

channelOut.write(buffer);

//将buffer中的数据读入到输出channelOut中,这时数据为position=15,limit=capacity=50,mark=-1;

buffer.clear();

//调用buffer.clear()之后,使得limit=position = 15,position=0,capacity = limit = 50,mark = -1

channelOut.write(buffer);

//使用write的时候操作buffer,是从position到limit的数据全部读入到输出通道中。所以,buffer中的值变为:position=limit = capacity =50,那么因为position=limit所以下次再读数据进buffer得时候,因为缓冲区是满的,所以无法读数据到缓冲区,造成了读入字节数为0,这样一直循环,就会一直把数据写入到文件中,并且造成文件空洞(文件中的数据之间有空隙)。而使得数据异常(不一致)。

}

当然flip在上篇文章中就介绍过.不过好像没有说道具体实现,现在看一下具体实现,其实就是上篇说到的怎么切换到读模式的

 publicfinal Buffer flip() {

       limit = position;

        position = 0;

        mark = -1;

        return this;

    }

下面这个方法,设置position=0,使得数据能够重写写入并且覆盖原来的写入,也可以使得读取的时候从头开始重复读取。

 publicfinal Buffer rewind() {

        position = 0;

        mark = -1;

        return this;

    }

下面这个函数有两层含义:一个是当读取的时候,表示还有多少字节没有读过;写模式的时候表示还剩余多少空间可写。

publicfinal int remaining() {

        return limit -position;

    }

获取buffer的可读属性,判断是否为只读

publicabstract boolean isReadOnly();

设置当前位置之后的多少位:功能跟position(int n)一样

finalint nextGetIndex() {                         // package-private

        if (position >=limit)

           throw new BufferUnderflowException();

        return position++;

    }

   finalint nextGetIndex(intnb) {                   // package-private

        if (limit -position < nb)

           throw new BufferUnderflowException();

        int p = position;

        position += nb;

        return p;

    }

下面这两个函数跟上面的完全一样,就是逻辑上一个是读一个是写

 finalint nextPutIndex() {                         // package-private

        if (position >=limit)

           throw new BufferOverflowException();

        return position++;

    }


    final int nextPutIndex(intnb) {                   // package-private

        if (limit -position < nb)

           throw new BufferOverflowException();

        int p = position;

        position += nb;

        return p;

    }

检测从i开始的第nb个位置的数据是否合法

finalint checkIndex(inti,int nb) {              // package-private

        if ((i < 0) || (nb >limit -i))

           throw new IndexOutOfBoundsException();

        return i;

    }

截断缓冲区,并且调用这个方法后,这个缓冲区就会变的不可用

 finalvoid truncate() {                            // package-private

        mark = -1;

        position = 0;

        limit = 0;

        capacity = 0;

    }

总得来说,buffer的这些方法都是围绕了buffer得对象模型展开的,主要操作元素就是对象中的mark,position,limit以及capacity属性。理解了这层含义,那么对于buffer的操作便会顺心应手。











0 0
原创粉丝点击