概述
基本属性
一个缓冲区Buffer有四个属性,容量(Capacity),上界(limit),位置(position),标记(mark)。
属性名 | 读模式含义 | 写模式含义 | 备注 |
容量(Capacity) | Buffer的容量就是一个Buffer所最多能容纳的数据长度。 | 同读模式 | Buffer在创建之后,容量便不会再变化 |
上界(limit) | limit表示Buffer最多能读到多少数据。 | limit表示最多能往Buffer写入的数据量,此时,limit等于Buffer的capacity | 当切换Buffer到读模式时,limit会被设置成写模式下的position值;表示可读数据的结束位置 |
位置(position) | position表示开始读取数据的位置,当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。当position=limit时,表示Buffer已无数据可读 | position表示当前待写入的位置。初始position值为0,当数据写入后,position会递增,position的max值为capacity – 1 | |
标记(mark) | mark(标记)是一个备忘位置,在初始化的时候,mark是未定义的值,在调用mark()函数后,设置mark=postion;等Buffer的position变化后,通过reset()函数,可以重新设置position=mark | 读写模式相同 | mark只是提供了一种position备份功能 |
Buffer模式
在Java中,一个缓冲区有两种模式,写模式(write mode)和读模式(Read Mode)。
当Buffer处于写模式的时候,表示当前Buffer等待写入数据,当一个Buffer初始化的时候,是处于写模式的。当Buffer达到容量最大值时,Buffer便不能再写入数据,如果继续写入,则会抛出BufferOverflowException异常,表示,Buffer已经写满。
当Buffer写完数据之后,可以切换到读模式,读模式表示可以从Buffer读取数据。如果Buffer内容已经读完,继续读取Buffer,则会抛出BufferUnderflowException异常。
模式切换函数
函数名 | 作用 |
filp | 将Buffer从写模式切换到读模式 |
clear | 将Buffer从读模式切换到写模式,同时清空Buffer内所有数据 |
compact | 将Buffer从读模式切换到写模式,只清空已读数据,保留未读数据 |
例子
下面以CharBuffer为例看下Buffer基本读写方式,以及对应的属性。
public class LearnBuffer {
static void bufferP(){
CharBuffer charBuffer = CharBuffer.allocate(64);
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position());
charBuffer.append("This is a test.");
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
System.out.println("\nBegin test read mode. ---------");
charBuffer.flip();
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
System.out.print(charBuffer.get());
System.out.println(charBuffer.get());
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
// mark当前位置
charBuffer.mark();
while (charBuffer.hasRemaining()){
System.out.print(charBuffer.get());
}
System.out.println();
try {
System.out.println(charBuffer.get());
}catch (java.nio.BufferUnderflowException e){
System.out.println("Get a java.nio.BufferUnderflowException.");
}
System.out.println("\nBegin test mark,reset. ---------");
// 将位置重置为之前mark的位置
charBuffer.reset();
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
while (charBuffer.hasRemaining()){
System.out.print(charBuffer.get());
}
System.out.println();
System.out.println("\nBegin test compact. ---------");
// 将位置重置为之前mark的位置
charBuffer.reset();
// 将已读数据清除,保留未读数据
charBuffer.compact();
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
charBuffer.flip();
while (charBuffer.hasRemaining()){
System.out.print(charBuffer.get());
}
System.out.println();
System.out.println("\nBegin test clear. ---------");
//读完数据之后,可以调用clear将Buffer切换到写模式,同时删除之前所有数据
charBuffer.clear();
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
}
public static void main( String[] args ) throws IOException {
bufferP();
}
}
下面是上面程序的输出结果
Capacity:64, limit:64, position:0
Capacity:64, limit:64, position:15
Begin test read mode. ---------
Capacity:64, limit:15, position:0
Th
Capacity:64, limit:15, position:2
is is a test.
Get a java.nio.BufferUnderflowException.
Begin test mark,reset. ---------
Capacity:64, limit:15, position:2
is is a test.
Begin test compact. ---------
Capacity:64, limit:64, position:13
is is a test.
Begin test clear. ---------
Capacity:64, limit:64, position:0
创建缓冲区
Java语言提供了8种缓存区。除了MappedByteBuffer是内存映射,其他7种对应了Java的7种内置类型。7种对应内置类型的Buffer都是抽象类,不能直接用new进行实例化。这7个类提供了相应的静态工厂方法来创建相应的类。每个类的工厂方法名都是相同的。
Java提供了静态工厂方法,用于新建Buffer对象。
allocate 方法可以分配一个指定容量的Buffer,
wrap 会直接传入一个数组,则Buffer并不会再分配新的内存创建缓冲区,而是直接使用传入的数组作为缓存区,创建的Buffer类的capacity为数组的长度,使用Buffer类的操作,都会直接影响到传入的数组,比如,Buffer.put了一个字符,则可以直接通过数组得到。
public class LearnBuffer {
static void createBuffer(){
char[] chars = new char[64];
CharBuffer charBuffer = CharBuffer.wrap(chars);
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
charBuffer.append('a');
System.out.println(chars[0]);
}
public static void main( String[] args ) {
createBuffer();
}
}
wrap 函数有两个版本,一个是直接传入一个数组,则buffer的position为0,limit为capacity;还有一种带有offset和length的版本,则buffer的position为offset,limit为length。
public class LearnBuffer {
static void createBuffer(){
char[] chars = new char[64];
CharBuffer charBuffer = CharBuffer.wrap(chars, 2, 16);
System.out.println("Capacity:"+charBuffer.capacity()+", limit:"+charBuffer.limit()
+", position:"+charBuffer.position() );
charBuffer.append('a');
System.out.println(chars[2]);
CharBuffer charBuffer1 = CharBuffer.wrap(chars);
System.out.println("Capacity:"+charBuffer1.capacity()+", limit:"+charBuffer1.limit()
+", position:"+charBuffer1.position());
}
public static void main( String[] args ) {
createBuffer();
}
}
复制缓存区
duplicate 函数
duplicate()函数创建一个与原始缓冲区相似的新缓冲区,新缓冲区共享旧缓冲区的数据和容量,但却有自己的position和limit。
public class LearnBuffer {
static void duplicateBuffer(){
CharBuffer charBuffer = CharBuffer.allocate(64);
charBuffer.append("This is a duplicate test.");
System.out.println("charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
CharBuffer newBuffer = charBuffer.duplicate();
System.out.println("newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
charBuffer.flip();
System.out.println("After charBuffer flip, charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("After charBuffer flip, newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
System.out.println(charBuffer.get(0));
newBuffer.put(0, 'H');
System.out.println(charBuffer.get(0));
}
public static void main( String[] args ) {
duplicateBuffer();
}
}
asReadOnlyBuffer()函数生成一个只读的缓冲区,和duplicate函数一样,新的只读缓冲区,和之前缓冲区共享数据和容量,各自拥有自己的position和limit。但只读缓冲区是不能调用put函数,否则会抛出ReadOnlyBufferException异常。
public class LearnBuffer {
static void readOnlyBuffer(){
CharBuffer charBuffer = CharBuffer.allocate(64);
charBuffer.append("This is a readonly test.");
CharBuffer newBuffer = charBuffer.asReadOnlyBuffer();
System.out.println("charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
charBuffer.flip();
System.out.println("After charBuffer flip, charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("After charBuffer flip, newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
try {
newBuffer.append('a');
}catch (ReadOnlyBufferException e){
System.out.println("Get an java.nio.ReadOnlyBufferException");
}
}
public static void main( String[] args ) {
readOnlyBuffer();
}
}
slice()函数也是产生新的缓冲区,并且与旧缓冲区共享数据,但slice()产生的缓冲区的容量是原缓冲区limit-position
static public void sliceBuffer(){
CharBuffer charBuffer = CharBuffer.allocate(64);
charBuffer.append("This is a slice test.");
CharBuffer newBuffer = charBuffer.slice();
System.out.println("charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
charBuffer.append("Add new string.");
System.out.println(newBuffer.get(0));
System.out.println("charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("newBuffer capacity:"+newBuffer.capacity()+", position:"+newBuffer.position()+", limit:"+newBuffer.limit());
charBuffer.flip();
CharBuffer newBuffer2 = charBuffer.slice();
System.out.println("charBuffer capacity:"+charBuffer.capacity()+", position:"+charBuffer.position()+", limit:"+charBuffer.limit());
System.out.println("newBuffer2 capacity:"+newBuffer2.capacity()+", position:"+newBuffer2.position()+", limit:"+newBuffer2.limit());
}