NIO:缓冲区ByteBuffer方法讲解

       NIO是jdk1.4后引入,是为了解决普通IO效率慢的问题。NIO将最耗时的IO操作(提取和填充缓冲区)转移回操作系统,让操作系统来完成这个操作因此可以极大的提高效率。NIO是以块为单位处理数据,传统IO是以字节为单位,所以NIO更快。
       NIO是通过通道Channel来读取和写入数据的,通道就可以类比传统的IO流,通道是直接和操作系统对接的,我们不会直接从通道中读取和写入数据,而是通过缓冲区Buffer。读取数据时,不会直接从通道中读取数据,而是将数据从通道读入到缓冲区,再从缓冲区拿数据。写数据也一样,通过缓存写入到通道中。
       我们首先需要做的就是处理好缓冲区,缓冲区其实就是对数组进行了一定封装,这样整体处理起来比较方便。Java提供的缓冲区类是Buffer,使用时都是使用其子类,比如ByteBuffer,CharBuffer,每种基本数据类型都有其对应的缓冲类型(boolean除外)。
       下面看看如何使用,以ByteBuffer举例
首先要说的是四个属性:mark,position,limit,capacity
mark是一个标记,调用reset方法可以让position重新回到mark位置
position是当前位置,指向缓存数组
limit是限制指针,通过Buffer读和写都是从position到limit,不能超过limit
capacity是当前缓存的容量大小
四个指针的关系 mark<=position<=limit<=capacity
这四个怎么用,先看这几个操作,几个例子就明白了
初始化

//ByteBuffer是通过分配内存来初始化的,这样就会new出来一个子类HeapByteBuffer,在堆上创建该对象
//感兴趣的看看源码
//分配缓冲区空间:创建一个字节缓冲区,申请内存空间为8字节
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
//在堆外创建对象,不受垃圾回收的限制
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
//通过字节数组初始化ByteBuffer对象
public static ByteBuffer wrap(byte[] array,int offset, int length)
public static ByteBuffer wrap(byte[] array)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
写入数据

//向缓冲区中写入一个字节数据
//还有其他重载方法:指定位置向缓冲中添加字节,向缓存中添加字节数组,向缓存中添加另外一个缓存
 byteBuffer.put((byte)10);
 //指定位置向缓冲区中添加字节,在3号位置添加10
 byteBuffer.put(3,(byte)10);
 //向一个缓存中添加另外一个缓存
 byteBuffer2.put(byteBuffer);

需要注意的是指定索引添加元素不会引起position位置的变化,不指定索引position位置会加1
在这里插入图片描述
在这里插入图片描述
获取缓存中的值

//获取缓冲中的数据,通过获取index位置的数据
byte b = byteBuffer.get(index);
//不指定索引获取缓冲数据

同样注意的是,不指定索引会引起position位置的变化,获取指定位置的数据不会引起position位置的变化
get操作源码:
在这里插入图片描述
还有几个函数需要了解一下

position():返回当前position的值
limit():返回当前limit的值
capacity():返回当前capacity的值
hasRemaining():返回position和limit之间是否有元素 即: return position < limit
remaining():返回position和limit之间的元素数  即:return limit - position

通过例子来了解上面的三个属性是做什么用的

public class NIO_Demo {
    public static void main(String[] args) {
        //分配缓冲区空间:创建一个字节缓冲区,申请内存空间为8字节
        ByteBuffer byteBuffer = ByteBuffer.allocate(8);
        //向缓冲区中写入数据
        byteBuffer.put((byte)10);
        byteBuffer.put((byte)20);
        byteBuffer.put((byte)30);
        byteBuffer.put((byte)40);
        //输出三个状态
        System.out.println("position:"+byteBuffer.position());
        System.out.println("limit:"+byteBuffer.limit());
        System.out.println("capacity:"+byteBuffer.capacity());

        //hasRemaining()告知在当前位置和限制之间是否有元素
        if(byteBuffer.hasRemaining()) {
            //remaining()返回当前位置与限制之间的元素数
            for (int i = 0; i < byteBuffer.remaining(); i++) {
                byte b = byteBuffer.get(i);
                System.out.println(b);
            }
        }
    }
}

在这里插入图片描述
可以看到当写入4个字节数后,position已经处于4号位置,capacity是8很好理解,因为分配了8个字节的内存,那么limit为什么也是8,有什么用?
limit的作用:为什么上面说limit是一个中转指针,当需要把缓存中数据写入到内核空间时,数据的有效范围是从position到limit,那么现在来说就是从4到8,可是很明显4-8是没有数据的,数据放在0-4,这个时候limit就要起作用了,不过需要调用一个函数flip

flip():反转此缓冲区   操作很简单: limit=position,position=0

执行完这个函数,拿到的值就正确了。
同样是上面的例子

        System.out.println("position:"+byteBuffer.position());
        System.out.println("limit:"+byteBuffer.limit());
        System.out.println("capacity:"+byteBuffer.capacity());
        //缓冲区的反转:转为有效数组   limit=position position=0 有效数据为position-limit
        byteBuffer.flip();
        //输出三个状态
        System.out.println("position:"+byteBuffer.position());
        System.out.println("limit:"+byteBuffer.limit());
        System.out.println("capacity:"+byteBuffer.capacity());

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42220532/article/details/90489461