NIO 数据存储结构——缓冲区 Buffer

一 点睛

NIO,可以称为 New IO 或 Non Blocking IO,是在 JDK 1.4 后提供的新 API。传统的I/O 是阻塞式的 I/O、面向流的操作;而 NIO 是非阻塞 I/O 、面向通道(Channel) 和 缓冲区(Buffer) 的操作。此外,NIO 还提供了选择器(Selector)全新的概念,这些都使 NIO 在传输数据时更加高效。下图为选择器、通道和缓冲区的关系图。

二 NIO 数据存储结构——缓冲区 Buffer

Buffer 的底层是一个数组,用于存储数据。NIO 提供了 7 种类型的缓冲区,用于存储不同类型的数据:ByteBuffer、IntBuffer、ShortBuffer、LongBuffer、FloatBuffer、DoubleBuffer、CharBuffer,并且它们都继承自 java.nio.Buffer。

在 Buffer 类中有 5 个重要的属性。

1 int position:下一个将要被读或写的元素位置。

2 int limit:限制 Buffer 中能够存放的元素个数,换句话说,limit 及之后的位置不能使用。

3 int capacity:Buffer 的最大容量,并且在创建后不能更改。

4 int mark:标记,可以在 Buffer 设置一个标记,之后可以通过 reset() 方法返回到列表该标记的位置。

5 long address:堆外内存的地址。

三 缓存区的常用方法

以 ByteBuffer 为例进行说明

方法

简介

public static ByteBuffer allocate(int capacity)

分配大小为 capacity 的非直接缓冲区(单位byte)

public static ByteBuffer allocateDirect(int capacity)

分配大小为 capacity 的直接缓冲区(单位byte)

public abstract ByteBuffer put(byte b)  

public abstract ByteBuffer put(int index, byte b)

public final ByteBuffer put(byte[] src)

public ByteBuffer put(byte[] src, int offset, int length)

向缓冲区中存放数据

public abstract byte get()

public abstract byte get(int index)

public ByteBuffer get(byte[] dst)

public ByteBuffer put(byte[] src, int offset, int length)

从缓存区中读取数据

public abstract ByteBuffer asReadOnlyBuffer()

将一个 Buffer 转为一个只读 Buffer。之后,不能再对转换后的 Buffer 进行写操作

public abstract ByteBuffer slice()

将原 Buffer 从 position 到 limit 之间的部分数据交给一个新的 Buffer 引用。也就是说,此方法返回的 Buffer 引用的数据,是原 Buffer 的一个子集。并且,新的 Buffer 引用和原 Buffer 引用共享相同的数据。

public static ByteBuffer wrap(byte[] array)

返回一个内容为 array 的 Buffer。此外,如果修改缓冲区的内容,array 也会随着改变,反之亦然。

public final Buffer flip()

将写模式转换成读模式

public final Buffer rewind()

用于重复读,具体重现参考源码

public final Buffer clear()

清空 Buffer,具体重现参考源码

public final Buffer mark()

标记:mark = position,具体重现参考源码

public final Buffer reset()

重置:position = mark,具体重现参考源码

四 基本演示

1 代码

public class NIODemo {
    public static void test1() {
        ByteBuffer buffer = ByteBuffer.allocate(100);
        System.out.println("---allocate----");
        System.out.println("position:" + buffer.position());  // position:0
        System.out.println("limit:" + buffer.limit()); // limit:100
        System.out.println("capacity(定义之后,不会再改变):" + buffer.capacity()); // capacity(定义之后,不会再改变):100

        // 向Buffer中存放数据
        System.out.println("---put()----");
        buffer.put("helloworld".getBytes());  
        System.out.println("position:" + buffer.position()); // position:10
        System.out.println("limit:" + buffer.limit());  // limit:100

        // 切换到读模式
        System.out.println("---flip()----");
        buffer.flip();
        System.out.println("position:" + buffer.position()); // position:0
        System.out.println("limit:" + buffer.limit()); // limit:10

        // 从Buffer中读取数据
        System.out.println("---get()----");
        byte[] bs = new byte[buffer.limit()];
        buffer.get(bs); // 读取数据,同时会后移position
        System.out.println("读取到的数据:" + new String(bs)); // 读取到的数据:helloworld
        System.out.println("position:" + buffer.position()); // position:10
        System.out.println("limit:" + buffer.limit()); // limit:10

        System.out.println("----slice()---");
        buffer = ByteBuffer.allocate(8);
        // buffer:0,1,2,3,4,5,6,7
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put((byte) i);
        }
        buffer.position(2);
        buffer.limit(6);
        // sliceBuffer:2,3,4,5;获取从 position 到 limit之间 buffer的引用。
        ByteBuffer sliceBuffer = buffer.slice();
        // sliceBuffer 与 原Buffer 共享相同的数据;即修改 sliceBuffer 中的数据时,buffer 也会改变。
        for (int i = 0; i < sliceBuffer.capacity(); i++) {
            byte b = sliceBuffer.get(i);
            b += 100;
            sliceBuffer.put(i, b);
        }

        // 测试
        System.out.println("当修改了 sliceBuffer 之后,查看 buffer:");
        buffer.position(0);
        buffer.limit(buffer.capacity());
        while (buffer.hasRemaining()) {//{x,x,x,x,x,x}  buffer.hasRemaining():判断是否有剩余元素
            System.out.print(buffer.get() + ","); // 0,1,102,103,104,105,6,7,
        }
        System.out.println();

        System.out.println("----mark--------");
        ByteBuffer buffer2 = ByteBuffer.allocate(100);
        buffer2.put("abcdefg".getBytes());

        // 在此时的 position 位置处,做一个标记 mark
        buffer2.mark();
        System.out.println("position:" + buffer2.position()); // position:7
        System.out.println("mark:" + buffer2.mark().position()); // mark:7

        /*
         通过get(byte[] dst, int offset, int length)方法,读取buffer中的“cde”。
         注意,此方法可以直接从 Buffer 中的指定位置 offset 开始读取数据,而不需要flip()或rewind()。
        */
        buffer2.get(bs, 2, 3);
        buffer2.reset(); // 恢复到 position 的位置 7
        System.out.println("position:" + buffer2.position()); // position:7
        System.out.println("mark:" + buffer2.mark().position()); // mark:7

        // 判断缓冲区是否有剩余数据
        if (buffer2.hasRemaining()) {
            System.out.println("Buffer中的剩余空间数:" + buffer2.remaining()); // Buffer中的剩余空间数:93
        }

        // 重复读rewind() : 1.postion=0,2.取消mark()
        System.out.println("---rewind()----"); 
        buffer2.rewind();
        System.out.println("position:" + buffer2.position()); // position:0

        // clear()"清空"缓冲区
        System.out.println("-------clear()--------");
        ByteBuffer buffer3 = ByteBuffer.allocate(100);
        buffer3.put("abc".getBytes());
        buffer3.clear(); // "清空"缓冲区 :position=0,但数据并没有真正被删除,只是处于废弃状态
        System.out.println("position:" + buffer3.position()); // position:0
        System.out.println("limit:" + buffer3.limit()); // limit:100
        System.out.println("clear()之后,仍然可以获取到Buffer中的数据:" + (char) buffer3.get(1)); // clear()之后,仍然可以获取到Buffer中的数据:b
    }
    public static void main(String[] args) throws IOException {
        test1();
    }
}

2 测试结果

---allocate----

position:0

limit:100

capacity(定义之后,不会再改变):100

---put()----

position:10

limit:100

---flip()----

position:0

limit:10

---get()----

读取到的数据:helloworld

position:10

limit:10

----slice()---

当修改了sliceBuffer之后,查看buffer:

0,1,102,103,104,105,6,7,

----mark--------

position:7

mark:7

position:7

mark:7

Buffer中的剩余空间数:93

---rewind()----

position:0

-------clear()--------

position:0

limit:100

clear()之后,仍然可以获取到Buffer中的数据:b

五 Buffer 存储各类型数据

1 代码

package nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
import java.nio.charset.CharacterCodingException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NIODemo {
    // Buffer 存储各类型数据
    public static void test() throws CharacterCodingException {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.putDouble(3.14159);
        buffer.putChar('程');
        buffer.flip();
        // 必须保证 get 和 put 的顺序一致
        System.out.println(buffer.getDouble());
        System.out.println(buffer.getChar());
    }

    public static void main(String[] args) throws IOException {
        test();
    }
}

2 测试

3.14159

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值