我们都直到,BIO
面向流的,而NIO
则是面向缓冲区的。所以我们需要一个数据容器来作为缓冲来存储即将写入或者读入的数据。对于java
的基本数据类型都有一种对应的buffer,通过我们使用最多的是byteBuffer
下面我们来演示下ByteBuffer
的使用情况:
public class ByteBufferDemo {
public static void main(String[] args) {
ByteBuffer b = ByteBuffer.allocate(26);
for(int i = 65;i<b.capacity() + 65;i++){
//往buffer添加一个字节的数据
b.put((byte) i);
}
System.out.println("position:" + b.position() + ",limit:"+ b.limit());
//切换到读模式 将limit 置为当前的position 然后position = 0
b.flip();
byte[] b1 = new byte[26];
System.out.println("position:" + b.position() + ",limit:"+ b.limit());
//将数据读入到数组中 读取从position到limit之间的值 如果可读的长度小于 b1的长度,那么将会抛出 BufferUnderflowException
b.get(b1);
System.out.println(new String(b1));
}
}
上面的代码我们演示了向缓冲区中添加了 26个大写的英文字母。运行该方法我们可以得到以下输出:
position:26,limit:26
position:0,limit:26
ABCDEFGHIJKLMNOPQRSTUVWXYZ
这里我们只进行一次读写,如果我们还需再次写入数据该怎么办?
调用buffer的clear()方法,将其所有的位置属性恢复到初始状态。clear方法的源码如下:
public Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
buffer的四个基本属性讲解:
- mark:标记,调用mark()来设置mark=position,再调用reset()可以position恢复到标记的位置
- position:位置,下一个读或者写的索引,每次读写缓冲区数据时都会改变该值
- limit:表示缓冲区的当前终点,不能对超过该位置区域进行读写,且极限是可以修改的。
- capacity:容量,即可以容纳的最大数据量,在创建的时候确定不能被修改
以上的这几个属性遵循的恒等式:
mark <= position <= limit < capacity
缓冲区的分配
我们可以直接调用allocation()方法来直接创建一个缓冲区。对于ByteBuffer``来说,就是实例化HeapByteBuffer
对象。此外,我们还可以通过wrap方法将一个现有的数据包装成buffer对象,实际上也是创建了一个HeapByteBuffer
对象。
@Test
public void testAllocation(){
//1、直接分配缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(26);
// 2、将现有的数组包装成一个buffer
ByteBuffer wrap = ByteBuffer.wrap(new byte[20]);
}
缓冲区分片
NIO中,我们还可以对一个缓冲区进行分片。即在现有的缓冲区上切出一个新的缓冲区,他们共享底层的数据,也就是说子缓冲区相当于原缓冲区的一个视图窗口。
/**
* 测试缓冲区分片 ,子缓冲区和父缓冲区是共享数据的
*/
@Test
public void testView(){
ByteBuffer byteBuffer = ByteBuffer.allocate(26);
for (byte i = 65;i<65+byteBuffer.capacity();i++){
byteBuffer.put(i);
}
byteBuffer.flip();
//获取一个子缓冲区
ByteBuffer slice = byteBuffer.slice();
byte[] read = read(byteBuffer);
System.out.println(new String(read));
//更改子缓冲区的中数值 这里放入的是26个小写字母
for(byte i=97;i<97+slice.capacity();i++){
slice.put(i);
}
byteBuffer.position(0);
byte[] bytes = read(byteBuffer);
//如果打印出来的是小写字母,那么说明他们是共享同一个数组
System.out.println(new String(bytes));
}
/**
* 从buffer将数据读取到数组中
* @param buffer
* @return
*/
public static byte[] read(ByteBuffer buffer){
byte[] bytes = new byte[buffer.limit() - buffer.position()];
buffer.get(bytes);
return bytes;
}
只读缓冲区
只读缓冲区是一个只能读取不能写入的buffer。通过调用缓冲区的asReadOnlyBuffer()方法来获取。如果向一个只读的buffer中写入数据,将会抛java.nio.ReadOnlyBufferException
@Test
public void testReadOnly(){
ByteBuffer buffer = ByteBuffer.allocate(26);
for(byte i= 65;i<65 + 20;i++){
buffer.put(i);
}
ByteBuffer readOnly = buffer.asReadOnlyBuffer();
// 抛出 java.nio.ReadOnlyBufferException
readOnly.put((byte) 97);
}
直接缓冲区
直接缓冲区是用来加快I/O速度,使用特殊方式分配内存的缓冲区。通常是通过调用al
/*
测试直接缓冲区
*/
@Test
public void testAllocateDirect(){
ByteBuffer buffer = ByteBuffer.allocateDirect(26);
for(byte i= 65;i<65 + buffer.capacity();i++){
buffer.put(i);
}
buffer.flip();
byte[] read = read(buffer);
System.out.println(new String(read));
}