2、向 buffer 中写数据
写数据到 Buffer 有两种方式:
(1)从 channel 写到 Buffer
(2)通过 Buffer 的 put 方法写到 Buffer 里。
从 Channel 写到 Buffer 的例子
int byteRead = channel.read(buf); // read into buffer
通过 put 方法写入 buffer 的例子:
buf.put(100);
put 的方法有很多版本,允许你不同的方式把数据写入到 buffer 中,例如,写到一个指定的位置,或者把字节数组写入到 Buffer .
3、flip() 方法
flip 方法将 Buffer 从写模式切换到读模式。调用 flip() 方法将会 position 设置为 0 , 并且将 limt 设置为之前 position 的值。换句话说,position 现在用于标记读的位置,limit 表示之前写进了多少个 byte, char 等(现在能读取多少个 byte, char 等)。
从 Buffer 中读取数据
从 Buffer 中读取数据到 Channel 中:
(1) 从 Buffer 中读取数据到 Channel
(2)使用 get 方法从 Buffer 中读取数据
从 Buffer 中读取数据到 Channnel 到例子:
// read form buffer into channel int bytesWritten = inChannel.write(buf);
使用 get() 方法从 Buffer 中读取数据的例子:
byte aByte = buf.get();
get 方法中有很多版本,允许你以不同的方式 Buffer 中读取数据。例如,从指定 position 读取,或者从 Buffer 中读取到字节数组。
Buffer 几个方法
1、rewind() 方法
Buffer.rewind() 将 position 返回0, 所以你可以重读 Buffer 中的所有数据。limit 保持不变,仍然表示能从 Buffer 中读取到多少个元素(byte, char 等)。
2、clear() 与 compact() 方法
一旦读完 Buffer 中的数据,需要让 Buffer 准备好再次被写入。可以通过 clear() 或 compact() 方法来完成
如果调用的是 cleanr () 方法,position 兼备设置为 0 , limit 被设置成 capactiy 的值。换句话说,Buffer 被清空了。 Buffer 中的数据并未清除,只是这些标记高数我们从哪里开始往 Buffer 中写数据。
如果 Buffer 中有些数未读的数据,调用 clear() 方法,数据将 “被遗忘”,意味着不在有任何标记会告诉你那些数据被读过,那些还没有。
如果 Buffer 中依然有未读的数据,且后续还需要这些数据,但是此时想要先写这些数据,那么使用 compact() 方法。
compact() 方法将所有未读的数据拷贝到 Buffer 起始处。然后将 position 设置到最后一个未读元素正后面。 limit 属性依然像 clear() 方法一样。设置成 capacity. 现在 Buffer 准备好写数据了,但是不会覆盖未读的数据。
3、mark() 与 reset() 方法
通过调用 Buffer.mark() 方法,可以标记 Buffer 中的一个特定 position . 之后可以通过调用 Buffer.reset() 方法恢复到这个 position 例如:
buffer.mark(); // call buffer.get() a couple of times, e.g. during parsing buffer.reset(); // set position back to mark
缓冲区操作
1、缓冲区分片
在 NIO 中除了可以分配或者包装一个缓冲区对象外,还可以更具现有的缓冲区对象来创建一个子缓冲区,即现有缓冲区上切出一片来作为一个新的缓冲区,但现有的缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,也就是说,子缓冲区相当于是现有缓冲区的一个视图窗口。调用 slice() 方法可以创建一个子缓冲区。
// 缓冲区分片 @Test public void b01() { ByteBuffer buffer = ByteBuffer.allocate(10); // 放入数据 for (int i = 0; i < buffer.capacity(); i++) { buffer.put((byte) i); } // 创建子缓冲区 buffer.position(3); buffer.limit(7); ByteBuffer slice = buffer.slice(); // 改变子缓冲区中的内容 for (int i = 0; i < slice.capacity(); i++) { byte b = slice.get(i); b *= 10; slice.put(i, b); } // 复位 buffer.position(0); buffer.limit(buffer.capacity()); while (buffer.remaining() > 0) { System.out.println(buffer.get()); } }
输出结果如下:
2、只读缓冲区
只读缓冲区非常简单,可以读取他们,但是不能向他们写入数据。可以通过调用缓冲区的 asReadOnlyBufer() 方法,将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。如果原缓冲区的内容发生了变化,只读缓冲区的内容随之发生变化:
// 只读缓冲区 @Test public void b02() { ByteBuffer buffer = ByteBuffer.allocate(10); // 放入数据 for (int i = 0; i < buffer.capacity(); i++) { buffer.put((byte) i); } // 创建一个只读缓冲区 ByteBuffer readOnlyBuf = buffer.asReadOnlyBuffer(); for (int i = 0; i < buffer.capacity(); i++) { byte b = buffer.get(i); b *= 10; buffer.put(i, b); } readOnlyBuf.position(0); readOnlyBuf.limit(readOnlyBuf.capacity()); while (readOnlyBuf.remaining() > 0) { System.out.println(readOnlyBuf.get()); } }
3、直接缓冲区
直接缓冲区为了加快 I/O 速度,使用一种特殊的方式为其分配内存的缓冲区, JDK 文档的描述为:给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中或者从一个中间缓冲区中拷贝数据。要分配直接缓冲区,需要调 allocatieDirect() 方法,而不是 alloacte() 方法,使用方式与普通缓冲区并无区别。
// 直接缓冲区,文件拷贝 @Test public void b03() throws IOException { String filePath = "/xx/01.txt"; FileInputStream inputStream = new FileInputStream(filePath); FileChannel fileInChannel = inputStream.getChannel(); String outPath = "/xx/02.txt"; FileOutputStream outputStream = new FileOutputStream(outPath); FileChannel fileOutChannel1 = outputStream.getChannel(); // 使用 allocateDirect , 而不是 allocate ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { buffer.clear(); int r = fileInChannel.read(buffer); if (r == -1) { break; } buffer.flip(); fileOutChannel1.write(buffer); } fileInChannel.close(); fileOutChannel1.close(); }
4、内存映射文件 I/O
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者通道的 I/O 快得多。内存映射 I/O 是通过使文件中的数据出现为内存数组的内容来完成的,这起初听起来师傅不过就是为了将整个文件读取到内容中,但是事实上并不是这样的。一般来说只有文件实际读取或者写入的部分才会映射到内存中。
// 内存映射文件 I/O @Test public void b04() throws IOException { String filePath = "/xxx/01.txt"; RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw"); FileChannel fileChannel = randomAccessFile.getChannel(); MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024); mappedByteBuffer.put(0, (byte) 97); mappedByteBuffer.put(1023, (byte) 122); fileChannel.close(); }