Java NIO读书笔记之Buffer缓冲区(一)
Java NIO 读书笔记
一、缓冲区:java.nio.Buffer
1.java.nio.Buffer是一个抽象类
public abstract Buffer{
public final int capicity(){
……
}
public final int position(){
……
}
public final Buffer posotion(int newPosition){
……
}
public final int limit(){
……
}
public final Buffer limit(int newLimit){
……
}
public final Buffer mark(){
……
}
public final Buffer reset(){
……
}
public final Buffer clear(){
……
}
public final Buffer flip(){
……
}
public final Buffer rewind(){
……
}
public final int remaining(){
……
}
public final boolean hasRemaining(){
……
}
public abstract boolean isReadOnly();
}
实现类:CharBuffer IntBuffer DoubleBuffer ShortBuffer LongBuffer FloatBuffer ByteBuffer
2.几个重要属性:
(1)容量:Capacity
缓冲区所能容纳数据元素的最大值,这一容量在缓冲区被创建时被指定,并且不能被修改。
(2)上界:Limit
缓冲区第一个不能被读或写的元素,即缓冲区现存元素的计数。
(3)位置:Position
下一个要被读或写的元素的索引。
(4)标记:Mark
一个备忘位置,调用mark()来设定mark = position,调用reset来设定position = mark;
PS:级联调用,Java NIO的方法都被设计为级联调用的方式,这些方法都会返回this
同样做法的还有StringBuffer对象
3.存取
通过当前的position属性,调用put的时候指出了下一个元素应该被插入的位置。调用get的时候指出下一个元素应该从何处检索。
但是在顶级的Buffer API当中,并没有包含get和put,每个继承Buffer的子类都有这两个方法,但每个方法所接受的参数和返回值在不同的
Buffer中是不一样的。
对于put方法超越上界,那么将会抛出BufferOverflowException
对于get方法超越上界,那么将会抛出BufferUnderfolowException
4.填充
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("Init Position:" + byteBuffer.position()); //初始的position值为0
byteBuffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put(((byte)'o'));
System.out.println("Current Position:" + byteBuffer.position()); //put之后指针移动到下一位,此时position值
为5
System.out.println("Capcity:" + byteBuffer.capacity());//capacity 值为5
System.out.println("Limit:" + byteBuffer.limit()); //limit 值为5
}
5.翻转
翻转的定义主要有两个:
(1)使position回归到0
(2)将上界属性设置为有效内容的末端
flip()方法能够将一个处于填充状态的缓冲区转换为准备释放状态的缓冲区
rewind()方法能够使Buffer的position回归到0,但是limit属性不变,通过rewind可以达到重读已经被flip了的缓冲区的目的。
6.释放
通过调用get()方法来释放缓冲区,如果一个缓冲区通过get()释放完毕,那么可以使用clear()方法来将缓冲区重置为空,一遍达到重复利
用缓冲区对象的目的。
来个组合拳:
import java.nio.ByteBuffer;
public class BufferTest {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("Init Position:" + byteBuffer.position());
byteBuffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put(((byte)'o'));
System.out.println("Current Position:" + byteBuffer.position());
System.out.println("Capcity:" + byteBuffer.capacity());
System.out.println("Limit:" + byteBuffer.limit());
byteBuffer.flip(); //翻转
System.out.println("After flip position:" + byteBuffer.position());
System.out.println("After flip limit:" + byteBuffer.limit());
for(int i = 0; i < byteBuffer.limit();i++){
byte b = byteBuffer.get();
System.out.print((char)b);
}
byteBuffer.rewind(); //rewind调整position之后可以再来一次
for(int i = 0; i < byteBuffer.limit();i++){
byte b = byteBuffer.get();
System.out.print((char)b);
}
byteBuffer.clear(); //clear之后重置了
System.out.println("After clear capaticy:" + byteBuffer.capacity());
System.out.println("After clear limit:" + byteBuffer.limit());
System.out.println("After clear position:" + byteBuffer.position());
}
}
7.压缩
压缩所应对的情景是只想从缓冲区中释放一部分数据,而不是全部,然后重新去填充,这时需要将未读的第一个元素的索引调整为0,通过
compact方法可以高效实现这种操作。
public static void main(String[] args){
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
byteBuffer.flip();
//取出两个元素来
for(int i = 0; i < 2; i++){
System.out.println((char)byteBuffer.get());
}
//执行压缩
byteBuffer.compact();
System.out.println("After compact capacity:" + byteBuffer.capacity()); //10
System.out.println("After compact limit:" + byteBuffer.limit()); //10
System.out.println("After compact position:" + byteBuffer.position()); //3 此时可以继续从Buffer末尾写入了
}
8.标记
public static void main(String[] args){
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
byteBuffer.position(2).mark();
System.out.println("Mark Position:" + byteBuffer.position()); //2
byteBuffer.position(4);
System.out.println("After modify, the position:" + byteBuffer.position()); //4
byteBuffer.reset(); //将position恢复到标记处
System.out.println("After reset, the position:" + byteBuffer.position()); //2
}
如果中途使用clear(),rewind()或flip()方法,均会使标记销毁,如果标记不存在或被销毁,那么调用reset时将会抛出
InvalidMarkException
9.比较
所有类型的缓冲区都提供了一个equals方法用来比较缓冲区是否相等,以及一个compareTo用来详细比较缓冲区。
使用equals来判断Buffer相等的条件:
(1)两个Buffer的类型相同
(2)两个Buffer剩余元素的数目必须相同,剩余元素的值 = posiotn - limit,与其他因素无关,包括capacity
(3)在每个缓冲区中应该被get返回的剩余数据元素序列必须是一致
public static void main(String[] args){
ByteBuffer buffer1 = ByteBuffer.allocate(10);
buffer1.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
buffer1.flip().position(1);
ByteBuffer buffer2 = ByteBuffer.allocate(6);
buffer2.put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
buffer2.flip();
System.out.println(buffer1.equals(buffer2)); //true
}
compareTo:如果一个缓冲区在不相等的元素被发现前已经被耗尽,那么较短的缓冲区被认为是小于较长的缓冲区。
10.批量移动
使用缓冲区的目的在于高效的移动数据,通过上面简单的get和put并不能高效的操作数据,buffer API提供了在缓冲区批量操作数据的接口
put
提供了两种方法的重载:
byteBuffer.put(myArray);
byteBuffer.put(myArray, offset, length)
如果缓冲区有足够的空间继续接受数据,那么数组元素将被复制到从当前位置开始的缓冲区,并且位置指针会移动,如果缓冲区没有足够的
空间,那么将会抛出BufferOverfolowException
Get:
byteBuffer.get(myArray);
byteBuffer.get(myArray, offset, length);
如果数组的长度大于缓冲区的limit,会抛出BufferUnderfollowException
如果数组的长度小于缓冲区的limit,会将整个数组填满,positon会指向未读的第一个元素。
public static void main(String[] args){
CharBuffer charBuffer = CharBuffer.allocate(10);
charBuffer.put('H').put(new char[]{'e','l','l','o'});
int limit = charBuffer.flip().limit();
char[] chars = new char[limit];
charBuffer.get(chars);
String str = new String(chars);
System.out.println(str);
}
CharBuffer还支持String类型的批量put操作
charBuffer.put(str);
charBuffer.put(str, offset, limit)