第二章 缓冲区(2)

2.1.2 Buffer API

现在让我们看看怎么使用一个Buffer。下面列举的是Buffer类的方法。

package java.nio;

public abstract class Buffer {

public final int capacity();

public final int position();

public final Buffer position(int pos);

public final int limit();

public final Buffer limit(int pos);

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();

}

首先需要注意的是那些通常需要返回void的方法,比如clear(),在这里返回的是一个Buffer引用。这个方法返回的是被调用对象的引用(即this)。这样的做法是一种类设计技术,可以允许程序员进行级联调用。如下面的代码:

buffer.mark();

buffer.position(5);

buffer.reset();

就可以被简写为:

buffer.mark().position(5).reset();

java.nio包中的类被设计成级联调用的形式。你可能在StringBuffer类中看到同样的用法。

注意:

      级联调用如果使用的好,可以产生简单、优雅、易读的代码。如果滥用也将会导致一堆不知所云的代码。一般使用级联调用是为了让你的代码更加具有可读性,让你的想法更清晰。如果级联调用降低了代码的清晰度,那么请不要使用它们。请时刻保证你代码的可读性。

对于上面的API需要注意还要isReadOnly()方法。所有的Buffer都是可读的,但不是所有的都是可写的。每个具体的Buffer子类都继承了isReadOnly()方法来标示这个Buffer是否能被修改。有一些类型的Buffer类可能并没有存储数据的数组。比如MappedByteBuffer的数据可能实际保存在只读的文件中。你也可以显示的创建一个只读的视图Buffer,来防止对数据的意外修改。试图对只读的Buffer类的修改将会导致ReadOnlyBufferException的抛出。但是我们要超越自己。

2.1.3 访问

让我们从基础开始。缓冲区管理着固定大小数目的元素。但是在特定的时间,我们可能只关心缓冲区中的一部分数据。也就是说,可能在移除数据之前,我们只对缓冲区填充了一部分数据。这时我们需要知道向缓冲区中添加了多少数据,下一个数据将放在哪里等等信息。位置(Position)属性就为我们提供了这样的功能。位置属性将决定当我们调用put()方法时下一个数据元素被插入的位置和调用get()方法时下一个元素从哪里取出。机敏的读者可能发现上面的BufferAPI中并没有get()put()方法。这是因为每一个Buffer子类都提供了这两个方法,只是因为每个Buffer子类处理的数据类型都不一样,因此每个子类的put()get()方法都有不同的参数和不同类型的返回值,因而他们不能被放入抽象的父类中。他们的定义必须遵从特定数据类型的子类。对于以下的讨论,我们将假定使用ByteBuffer类,这个类的方法被列在下面(它包含了get()方法和put()方法,对这两个方法的讨论将在2.1.10小节中讨论):

public abstract class ByteBuffer 

extends Buffer implements Comparable {

public abstract byte get();

public abstract byte get(int index);

public abstract ByteBuffer put(byte b);

public abstract ByteBuffer put(int index, byte b);

}

Getput访问数据的方法可以是相对的也可以是绝对的。就像前面列出的,相对版本的方法没有参数。当相对版本的方法被调用时,位置属性加上一。如果位置数据前进的太多,那么方法就会抛出异常。比如put方法,如果位置属性大于限制属性,那么方法就会抛出BufferOverflowException异常。而对于get方法,如果位置属性不小于限制属性,那么也将抛出BufferUnderflowException异常。绝对版本的方法不会改变位置属性,但是如果参数index不在有效的范围之内(比如是负数或者不小于限制属性),那么也将抛出IndexOutOfBoundsException异常。

2.1.4 填充

先举个例子。我们将代表“Hello”字符串的ascii码序列载入一个名为bufferByteBuffer对象。当下面的代码在图2.2中创建的buffer对象执行之后,buffer的状态将如图2.3所示的一样。

buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');

2.3 五次调用put之后的buffer

注意这个例子中所有的字符都被强制转换为字节类型。我们不能不经过转换,就像:

buffer.put('H');

因为我们存放的是字节而不是字符。记住在java中所有的字符内部表示都是unicode编码的,每个字符占两个字节。这一小节的例子使用的字节是跟ascii字符集数值值一样大小的量。通过将字符转换为字节,我们丢掉了字符的高8位,使用低8位来填充位置。这样做只适合Latin字符而不适合所有的unicode字符。为了让事情简化,我们暂时将避免讨论字符串映射问题。第六章中将包括字符集的详细内容。

既然我们的数据已经存放在buffer中了,如果我们想改变里面的数据但是又不想改变当前buffer的位置属性,该怎么办呢?绝对路径版本的put方法就可以达到这样的目的。假设我们想将bufferascii字符的数据从"Hello"改为"Mellow",我们可以像下面那样做:

buffer.put(0,(byte)'M').put((byte)'w');

我们使用绝对版本的put方法将位置为0的数据使用十六进制为0x4d代替,然后将0x77放入当前位置属性标示的位置,最后一个put方法将位置属性增加一。最后的结果如图2.4所示。

2.4 改变后的buffer

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值