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()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起 始处,新写入的数据将放到缓冲区未读数据的后面。
使用 Buffer 的例子
1. @Test 2. public void testConect2() throws IOException { 3. RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu/01.txt", "rw"); 4. FileChannel inChannel = aFile.getChannel(); 5. 6. //create buffer with capacity of 48 bytes 7. ByteBuffer buf = ByteBuffer.allocate(48); 8. int bytesRead = inChannel.read(buf); //read into buffer. 9. while (bytesRead != -1) { 10. buf.flip(); //make buffer ready for read 11. while(buf.hasRemaining()){ 12. System.out.print((char) buf.get()); // read 1 byte at a time 13. }buf.clear(); //make buffer ready for writing 14. bytesRead = inChannel.read(buf); 15. } 16. aFile.close(); 17. }
使用 IntBuffer 的例子
1. @Test 2. public void testConect3() throws IOException { 3. // 分配新的 int 缓冲区,参数为缓冲区容量 4. // 新缓冲区的当前位置将为零,其界限(限制位置)将为其容量。 5. // 它将具有一个底层实现数组,其数组偏移量将为零。 6. IntBuffer buffer = IntBuffer.allocate(8); 7. for (int i = 0; i < buffer.capacity(); ++i) { 8. int j = 2 * (i + 1); 9. // 将给定整数写入此缓冲区的当前位置,当前位置递增 10. buffer.put(j); 11. } 12. // 重设此缓冲区,将限制设置为当前位置,然后将当前位置设置为 0 13. buffer.flip(); 14. // 查看在当前位置和限制位置之间是否有元素 15. while (buffer.hasRemaining()) { 16. // 读取此缓冲区当前位置的整数,然后当前位置递增 17. int j = buffer.get(); 18. System.out.print(j + " "); 19. } 20. }
Buffer 的 capacity、position 和 limit
为了理解 Buffer 的工作原理,需要熟悉它的三个属性:
- Capacity
- Position
- limit
position 和 limit 的含义取决于 Buffer 处在读模式还是写模式。不管 Buffer 处在什么 模式,capacity 的含义总是一样的。
这里有一个关于 capacity,position 和 limit 在读写模式中的说明:
capacity
作为一个内存块,Buffer 有一个固定的大小值,也叫“capacity”.你只能往里写 capacity 个 byte、long,char 等类型。一旦 Buffer 满了,需要将其清空(通过读数 据或者清除数据)才能继续写数据往里写数据。
position
1)写数据到 Buffer 中时,position 表示写入数据的当前位置,position 的初始值为 0。当一个 byte、long 等数据写到 Buffer 后, position 会向下移动到下一个可插入 数据的 Buffer 单元。position 最大可为 capacity – 1(因为 position 的初始值为 0).
2)读数据到 Buffer 中时,position 表示读入数据的当前位置,如 position=2 时表 示已开始读入了 3 个 byte,或从第 3 个 byte 开始读取。通过 ByteBuffer.flip()切换到 读模式时 position 会被重置为 0,当 Buffer 从 position 读入数据后,position 会下 移到下一个可读入的数据 Buffer 单元。
limit
1)写数据时,limit 表示可对 Buffer 最多写入多少个数据。写模式下,limit 等于 Buffer 的 capacity。
2)读数据时,limit 表示 Buffer 里有多少可读数据(not null 的数据),因此能读到 之前写入的所有数据(limit 被设置成已写数据的数量,这个值在写模式下就是 position)。
Buffer 的类型
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
这些 Buffer 类型代表了不同的数据类型。换句话说,就是可以通过 char,short,int, long,float 或 double 类型来操作缓冲区中的字节。
Buffer 分配和读写数据
Buffer 分配
要想获得一个 Buffer 对象首先要进行分配。 每一个 Buffer 类都有一个 allocate 方法。 下面是一个分配 48 字节 capacity 的 ByteBuffer 的例子。
ByteBuffer buf = ByteBuffer.allocate(48);
这是分配一个可存储 1024 个字符的 CharBuffer:
CharBuffer buf = CharBuffer.allocate(1024);