了解缓冲区
NIO核心对象
在NIO中,缓冲区、选择器、通道是三个核心的对象。本文重点介绍Buffer缓冲区。
1. Buffer 简介
缓冲区可以理解为了一个基于数组的容器对象,在NIO库中,所有数据都是在缓冲区处理的。当要读取数据时,就把数据读到缓冲区,要写入数据时,也是把数据写到缓冲区。这与面向流的I/O系统中,所有数据都是直接写入或从流中读取是类似的。
在NIO中,缓冲区数据类型从抽象类Buffer继承,对于Java的基本类型,会有具体的Buffer类型与之对应,如:
- ByteBuffer
- LongBuffer
- DoubleBuffer
- CharBuffer
- FloatBuffer
- IntBuffer
- ShortBuffer
- MappedByteBuffer
最常用的可能就是ByteBuffer了。
2. 一段简单的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 + " ");
}
}
}
3. Buffer的重要属性
position
下一个将要被写入或读取的元素位置索引,当get()/put()的时候会自动更新值。
limit
缓冲区的限制,它的值小于或等于capacity。
capacity
最大容量。
mark
在缓冲区的位置设置标记。
4. 缓冲区的分配
allocate 用于创建一个指定大小的缓冲区,它的参数是数组大小,也可以使用wrap直接封装一个数组:
byte array[] = new byte[10];
ByteBuffer buffer2 = ByteBuffer.wrap(array);
5. 子缓冲区
可以在一个缓冲区上开辟一段子缓冲区,便于操作。
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
6. 只读缓冲区
buffer.asReadOnlyBuffer
7. 直接缓冲区
在虚拟机内存外开辟的内存,IO操作直接进行,不再对其进行复制,但创建和销毁开销大。
8. 内存映射
java中传统的文件操作使用File的I/O操作,JVM发起read()、write(),会调用操作系统的内核,当中存在数据传输。
从jdk1.4开始,提供了MappedByteBuffer,用于帮助建立一个从JVM空间到OS文件系统页的映射的虚拟内存。这种方式避免了因为拷贝文件内容的带来的开销。
OS使用虚拟内存在内核空间之外缓存文件,可以被非内核进行所共享。java直接映射文件页到MappedByteBuffer,然后处理文件,不需要将其加载到JVM里面。