NIO学习——Buffer的概念和IntBuffer的基本操作(一)

NIO是JDK1.4之后出现的,针对于BIO(同步阻塞IO)的改进,属于同步非阻塞IO。
当然,在JDK1.7开始的NIO2(又称为AIO),在ava.nio.channels包下,又增加了几个异步操作的Channel类,所以,AIO属于异步非阻塞IO操作。会在后续文章中讨论。

Buffer

Buffer继承自Object类,是基本类型元素的线性有限序列(容器)。除内容外,Buffer的基本属性有:Capacity—容量,Limit—限制,Position—位置。对着三个变量操作,可以完成几乎所有对Buffer的代码操作。

  • Capacity: 缓冲区的容量,即Buffer包含元素的数量。需要满足:capacity>=0 且 不能更改
  • Limit:是第一个不应该读取或写入元素的索引。需要满足:limit>=0 且 limit<=capacity
    比如,当前Buffer中有四个元素,那么第五个索引,即索引4,就是不应该读取或写入的索引值。
  • Position:下一个要读取或写入的元素的索引。position>=0 且 position<=limit
    比如,在4个元素的put操作之后,最后一个元素的索引为3,position会指向索引值4;flip()操作之后,索引重设,position指向0索引,始终指向下一个要读取或写入的元素的索引。

Buffer的子类

对于每个非 boolean 基本类型,此类都有一个子类与之对应。
包括:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer

下面以IntBuffer为例,来使用一下Buffer操作的基本API:

public class Demo1IntBuffer {
    /**
     * 1.写入数据之前的position、limit和capacity: position = 0, limit = 10, capacity = 10
     * 2.写入数据之后的position、limit和capacity: position = 4, limit = 10, capacity = 10
     * 3.准备输出数据时的position、limit和capacity: position = 0, limit = 4, capacity = 10
     * 缓冲区中的内容: 3,5,7,9,
     * 
     * @param args
     */
    public static void main(String[] args) {
        IntBuffer buffer = IntBuffer.allocate(10);// 准备大小为10的缓冲区
        System.out.println("1.写入数据之前的position、limit和capacity:");
        System.out.println("position = " + buffer.position() + ", limit = " + buffer.limit() + ", capacity = "
                + buffer.capacity());
        // 定义一个int数组
        int[] temp = { 5, 7, 9 };
        // 增加一个数据
        buffer.put(3);
        // 将temp也放入buffer中
        buffer.put(temp);

        System.out.println("2.写入数据之后的position、limit和capacity:");
        System.out.println("position = " + buffer.position() + ", limit = " + buffer.limit() + ", capacity = "
                + buffer.capacity());

        // flip方法重设缓冲区:重设之后,limit变成position,position则指向0索引
        buffer.flip();
        System.out.println("3.准备输出数据时的position、limit和capacity:");
        System.out.println("position = " + buffer.position() + ", limit = " + buffer.limit() + ", capacity = "
                + buffer.capacity());
        System.out.println("缓冲区中的内容:");
        while (buffer.hasRemaining()) {
            int i = buffer.get();
            System.out.print(i + ",");
        }
    }
}

创建子缓冲区:
子缓冲区,也称为共享子序列,其实就是创建新的int数组。

public class Demo2SubBuffer {

    public static void main(String[] args) {
        IntBuffer buf = IntBuffer.allocate(10);// 开辟大小为10的缓冲区
        IntBuffer sub = null; // 定义子缓冲区
        for (int i = 0; i < 10; i++) {
            buf.put(2 * i + 1);// 加入10个奇数:1 3 5 7 9 11 13 15 17 19 
        }

        //positon 和limit正好截取了buf中的:5 7 9 11
        buf.position(2);//主缓冲区指针设置在第3个元素
        buf.limit(6);//主缓冲区limit为6   

        sub = buf.slice();//创建新的int数组,子缓冲区,也称为共享子序列

        for (int i = 0; i < sub.capacity(); i++) {//sub.capacity()的值为4,sub的下标所以为0-3
            int temp = sub.get(i);//根据下表取得元素:分别取到 5,7,9,11
            //put : 将给定 int 写入此缓冲区的当前位置(所以是覆盖的,相当于修改),然后该位置递增。 
            sub.put(temp-1);//修改子缓冲区的内容 : 4,6,8,10
        }
        buf.flip();//重设缓冲区
        buf.limit(buf.capacity());//设置limit为capacity的大小,这里为10,按照limit的定义 10是第一个不应该读取或写入的元素的索引
        System.out.println("主缓冲区中的内容:");
        while(buf.hasRemaining()){
            int i = buf.get();//此处因为sub对main是相互可见的,所以会取到sub里面改变的值
            System.err.print(i+",");
        }
    }
}
/** 
主缓冲区中的内容:
1,3,4,6,8,10,13,15,17,19,
*/

[说明]
* 新缓冲区的内容将从主缓冲区的当前位置开始,这边也就是索引2 ,值是5。
* 此缓冲区内容的更改在新缓冲区中是可见的,反之亦然;
* 这两个缓冲区的位置、界限和标记值是相互独立的。
* 新缓冲区的位置将为零(从0开始),
* 其容量和界限将为此缓冲区中所剩余的 int 数量(在这容量就是4,limit也是4),
* 其标记是未定义的。

[特别注意] 为何遍历时候对将sub的元素由奇数变为偶数载put回去,不是追加而是修改?

    for (int i = 0; i < sub.capacity(); i++) {//sub.capacity()的值为4,sub的下标所以为0-3
         int temp = sub.get(i);//根据下表取得元素:分别取到 5,7,9,11
         sub.put(temp-1);//修改子缓冲区的内容 : 4,6,8,10
    }

在API中,PUT的定义是:

  • 将给定 int 写入此缓冲区的当前位置,然后该位置递增。
    所以,这边的put是覆盖原先当前索引上的值,相当于修改。

创建只读缓冲区
通过slice()方法创建的子缓冲区是可以被修改的,如果不希望缓冲区被修改呢?
这时候就需要通过主缓冲区对象调用asReadOnlyBuffer()方法创建只读缓冲区

public class Demo3ReadOnlyBuffer {
    public static void main(String[] args) {
        IntBuffer buf = IntBuffer.allocate(10);// 开辟大小为10的缓冲区
        IntBuffer read = null; // 定义子缓冲区
        for (int i = 0; i < 10; i++) {
            buf.put(2 * i + 1);// 加入10个奇数:1 3 5 7 9 11 13 15 17 19 
        }

        read = buf.asReadOnlyBuffer();//创建只读缓冲区

        read.flip();//重设
        System.out.println("只读缓冲区中的内容:");
        while(read.hasRemaining()){
            int i = read.get();
            System.err.print(i+",");
        }
        //此处如果对read中的元素进行修改,就报错
        read.put(20);
    }
}

如果对只读缓冲区中的元素进行put(也就是修改)操作,就会报错ReadOnlyBufferException
这里写图片描述

以上就是NIO的一些基本概念,和NIO中IntBuffer的基本操作。

猜你喜欢

转载自blog.csdn.net/Sora_Xu/article/details/81380269
今日推荐