Buffer 简介
Java NIO 中的 Buffer 用于和 NIO 通道进行交互。数据是通道读取到缓冲区,从缓冲区写入到通道中的。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装 NIO Buffer 对象,并且提供了一组方法,用来方便的访问这块内存。缓冲区世纪上一个容器对象,更直接的说,其实就是一个数组,在 NIO 库中,所有数据都是用缓冲区处理的。 在读取数据时,它直接读到缓冲区中;在写数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流 I/O 系统中,所有数据都是直接写入或者直接将数据读取到 Stream 对象中。
在 NIO 中,所有的缓冲区类型都是继承于抽象类 Buffer, 最常用的就是 ByteBuffer. 对于 Java 中国呢的基本类型,金额都有一个具体 Buffer 类型与之对应,他们之间的继承关系如下图所示:
Buffer 的基本方法
1、使用 Buffer 读写数据,一般遵循以下四个步骤:
(1)写数据到 Buffer
(2)调用flip() 方法
(3)从 Buffer 中读取数据
(4)调用 clear() 方法或者 compact() 方法
当向 buffer 写数据时,buffer 会记录下写了多少数据。一旦要读数据,需要通过 flip() 方法将 buffer 从写模式切换到读模式。在读模式下,可以读取到之前写入到 buffer 的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或者 compact() 方法。clear() 方法会清空整个缓冲区。compact() 方法只会清除已经度过的数据。任何未读取的数据都被移动到了缓冲区的起始处,新写的数据将放到缓冲区未读数据的后面。
2、使用 Buffer 的例子
@Test public void buffer01() throws IOException { // FileChannel String pathName = "/Users/zhengsh/sourcecode.io/zhengsh-vvip/nio/src/main/resources/01.txt"; RandomAccessFile accessFile = new RandomAccessFile(pathName, "rw"); FileChannel channel = accessFile.getChannel(); // 创建 buffer , 大小 ByteBuffer buffer = ByteBuffer.allocate(1024); // 读 int bytesRead = channel.read(buffer); while (bytesRead != -1) { // read 模式 buffer.flip(); while (buffer.hasRemaining()) { System.out.println((char) buffer.get()); } buffer.clear(); channel.read(buffer); } accessFile.close(); } @Test public void buffer02() { // 创建 buffer IntBuffer buffer = IntBuffer.allocate(8); for (int i = 0; i < buffer.capacity(); i++) { int j = 2 * (i + 1); buffer.put(j); } // 重置缓冲区 buffer.flip(); while (buffer.hasRemaining()) { int value = buffer.get(); System.out.println(value + " "); } }
Buffer 的 capactity、posittion 和limit
为了理解 Buffer 的工作原理,需要熟悉它的三个属性:
- capacity
- ponstition
- limit
position 和 limit 的含义取决于 Buffer 处在读模式还是写模式。不管 buffer 处于什么模式,capactity 的含义总是一样的。
这里有一个关于 capacity, postition 和 limit 在读模式中的说明:
(1) capactiy
作为一个内存块,Buffer 有一个固定的大小值,也叫做 “capactiy” . 你只能往里面写 capacity 个 byte
long、 char等类型。一旦 buffer 满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
(2) postition
1)写数据到 Bufer 中时,position 表示写入数据的当前位置,position 的初始值为 0 。当一个 byte,long,等数据写入到 buffer 后,position 会向下移动到下一个可插入的元素的 buffer 但愿。position 最大可为 capacity -1 (因为 position 的初始值为 0)
2)读数据到 Buffer 中时,position 表示读数据的当前位置,如 position = 2 时表示已经开始读了 3 个 byte, 或者从第三个 byte 开始读取,通过 ByteBuffer.flip() 切换到读模式 position 会被重置为 0, 当 Buffer 从 position 读入数据后,position 会下移到下一个可读入的数据 Buffer 单元。
(3) limit
1)写数据时, limit 表示可以对 Buffer 最多写入多少个数据。写模式下,limit 等于 Buffer 的 capactiy
2)读数据时, limit 表示 Buffer 里有多少可读数据(not null 的数据),因此能读取到之前写入的所有数据(limit 被设置为已写数据的数量,这个值在写模式下就是 position)
Buffer 的类型
Java NIO 有一下 Buffer 的类型
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- LongBuffer
- ShortBuffer
这些 buffer 类型都代表了不同的数据类型。换句话说,就是可以通过 char ,short, int, long , float 或者 double 类型来操作缓冲区的字节。
Buffer 分配和写数据
1、 Buffer 分配
想要获取一个 Buffer 对象首先要进行分配。每一个 Buffer 类都有一个 allocate 方法。
下面是一个分配 48 字节 capactiy 的 ByteBuffer 的例子。
ByteBuffer buf = ByteBuffer.alloacte(48);
这是分配一个可存储 1024 个字符的 CharBuffer:
ByteBuffer buf = ByteBuffer.alloacte(1024);