读书笔记-《Java NIO》:第二章 缓冲区(1)

来源:互联网 发布:端口号的目的是 编辑:程序博客网 时间:2024/06/05 01:07

上周生病停更了一周,这周我们继续^_^。

今天我们来看下第二章---缓冲区,了解下不同的缓冲区类型,并学会使用。


焦点1:Java NIO的Buffer有哪些类型?



Note: Buffer及其继承类CharBuffer、IntBuffer、DoubleBuffer、ShortBuffer、LongBuffer、FloatBuffer、ByteBuffer都是抽象类。


焦点2:不同类型的缓冲区有哪些公共的属性?



Buffer类相比于简单的数组,包含的内容更为丰富,除了数据内容外,还包含一些其他信息,比如操作数据的API,容量和上限等属性。


容量(Capacity):缓冲区可以容纳数据元素的最大个数,缓冲区创建时被设定,并且设定后永远不能改变

上界(Limit):缓冲区中第一个不能被读或写的元素;

位置(Position):下一个要被读或写的索引;

标记(Mark):设置的备忘位置,调用mark()时设定mark=position;调用reset()时设定position=mark;


这四个属性之间的关系:

0 <= mark <= position <= limit <= capacity


焦点3: 缓冲区有哪些常用的API?



书中提到,按照习惯用法,clear()应该返回void,这里怎么返回了Buffer呢?

答案是:这样做是为了使用级联,像这样Buffer.mark().reset().clear()。


所有的缓冲区都是可读的,但并非都可写,这就有了isReadOnly(),isReadOnly()是个abstract方法,意味着要想创建可实例化的子类,子类必须实现这个方法。


焦点4:常用的API使用简介



1)存取:

put(),get()应该是我们最常用的API,有没有发现什么问题?焦点3中的列举的顶层类Buffer类常用的API里面并不包含这两个方法,为什么?

细细想想就会明白,各个不同的Buffer类型,存取的类型是不一样的,不同的Buffer类型get()和put()的类型都不相同,所以就没法放到顶层类Buffer里了。

书中举了ByteBuffer类作为例子,如上图,get()返回的是特定的byte类型。

2)翻转

我们通过put将缓冲区填充为Hello,

现在我们想读取写入的内容,应该怎么做呢?直接使用get()?

先来ByteBuffer看下get()的定义,

Reads the byte at this buffer's current position, and then increments the position,如果使用get(),获取的将是index=5位置处的数据,并且获取后讲position加1,看上图,index=5的地方什么数据都没有。。。

所以直接使用get()是不行的,我们应该这样:

i>将position设置为0,也就是'H'的下标,让Buffer知道读的下界

ii>将当前位置(也就是index=5的地方)设置为limit,让Buffer知道读的上界

buffer.limit(buffer.position()).position(0);

实际上Buffer内部已经定义了一个函数flip(),实现了i>ii>两步操作。

我们可以直接使用buffer.flip()达到和使用buffer.limit(buffer.position()).position(0)一样的效果。

Buffer内部还有一个rewind()方法,和flip()效果差不多,同样是将position设置为0,但是不设置limit属性。

使用flip()后position和limit的位置,如下图。

3)压缩

试想你并不想读取全部的buffer数据,只是想读取部分,怎么做比较合理呢?

如下图,你已经读了M,e两个数据,不想再往下读了,想要让Buffer变为重新可写入的状态,你会怎么办?

i>将l,l,o,w向前移动,覆盖M,e;

ii>将position设置为4,因为向前移动后,最后一个可写的position就是下标为4的位置;

iii>将limit重新设置为capacity,代表Buffer可重新被填满。

别慌着自己去写这些实现,Buffer已经为我们定义好了一个方法compact(),调用buffer.compact()后,效果如下。

焦点5:缓冲区怎么创建?



我们以CharBuffer为例,讲3个创建方法

1)allocate()

CharBuffer buffer = CharBuffer.allocate(100);

这段代码表示从堆空间上分配一个char型数据作为备份存储器来存储100个char变量。

2)wrap()

如果你想使用自己创建的数组作为缓冲区的备份存储器,请使用这个wrap()。

char[] myArr = new char[100];

CharBuffer buffer = CharBuffer.wrap(myArr);

这里着重说一下CharBuffer.wrap(myArr, 12, 42)这个方法,这个方法并不像它看上去那样,使用了myArr数组的一个子集,它实际上使用的还是整个myArr数组。12,42只是设置了初始状态。


allocate()和wrap()创建的缓冲区都是间接的,间接的缓冲区使用备份数组。

这就引入了另一个方法hasArray(),这个方法就是告诉你这个缓冲区是否有一个可存取的备份数组,如果有,array()这个方法就会返回这个备份数组的引用。


今天先到这儿,明天继续^_^。


原创粉丝点击