NIO:缓冲区ByteBuffer方法讲解

    xiaoxiao2022-07-14  160

           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)

    堆内和堆外内存分配demo:

    写入数据

    //向缓冲区中写入一个字节数据 //还有其他重载方法:指定位置向缓冲中添加字节,向缓存中添加字节数组,向缓存中添加另外一个缓存 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());

    最新回复(0)