在NIO中,缓冲区、选择器、通道是三个核心的对象。本文重点介绍Buffer缓冲区。
缓冲区可以理解为了一个基于数组的容器对象,在NIO库中,所有数据都是在缓冲区处理的。当要读取数据时,就把数据读到缓冲区,要写入数据时,也是把数据写到缓冲区。这与面向流的I/O系统中,所有数据都是直接写入或从流中读取是类似的。
在NIO中,缓冲区数据类型从抽象类Buffer继承,对于Java的基本类型,会有具体的Buffer类型与之对应,如:
package org.example; import java.nio.IntBuffer; public class App { public static void main(String[] args) { // 分配一个32字节的缓冲区,position=0 IntBuffer buffer = IntBuffer.allocate(Integer.SIZE); for (int i = 0; i < buffer.capacity(); ++i) { int j = i * i; // 缓冲区放一个值 buffer.put(j); } // 重置缓冲区,position设为0 buffer.flip(); while (buffer.hasRemaining()) { // 每次读取后,position会自动指到下一个位置 int j = buffer.get(); System.out.print(j + " "); } } }
下一个将要被写入或读取的元素位置索引,当get()/put()的时候会自动更新值。
缓冲区的限制,它的值小于或等于capacity。
最大容量。
在缓冲区的位置设置标记。
allocate 用于创建一个指定大小的缓冲区,它的参数是数组大小,也可以使用wrap直接封装一个数组:
byte array[] = new byte[10]; ByteBuffer buffer2 = ByteBuffer.wrap(array);
可以在一个缓冲区上开辟一段子缓冲区,便于操作。
package org.example; import java.nio.ByteBuffer; import java.nio.IntBuffer; public class App { public static void main(String[] args) { IntBuffer buffer = IntBuffer.allocate(8); for(int i=0;i<buffer.capacity();++i){ buffer.put(i); } //创建子缓冲区 buffer.position(4); // buffer.limit(5); IntBuffer slice = buffer.slice(); // 把一个位置的平方 int tmp = slice.get(0); tmp = tmp*tmp; slice.put(0, tmp); buffer.position(0); buffer.limit(buffer.capacity()); while(buffer.remaining()>0){ System.out.println(buffer.position() + " = " + buffer.get()); } } }
返回值:
0 = 0 1 = 1 2 = 2 3 = 3 4 = 16 5 = 5 6 = 6 7 = 7
buffer.asReadOnlyBuffer
在虚拟机内存外开辟的内存,IO操作直接进行,不再对其进行复制,但创建和销毁开销大。
java中传统的文件操作使用File的I/O操作,JVM发起read()、write(),会调用操作系统的内核,当中存在数据传输。
从jdk1.4开始,提供了MappedByteBuffer,用于帮助建立一个从JVM空间到OS文件系统页的映射的虚拟内存。这种方式避免了因为拷贝文件内容的带来的开销。
OS使用虚拟内存在内核空间之外缓存文件,可以被非内核进行所共享。java直接映射文件页到MappedByteBuffer,然后处理文件,不需要将其加载到JVM里面。