缓冲区在概念上是一个顺序存储数据的地方(假设是字节),你可以向其中写入数据,并且可以从中读取数据。
(忘掉与“通道”的“读取”和“写入”的任何关联。当我说“读取”时,我指的是Buffer.get,当我说“写入”时,我指的是Buffer.put)
常见的用例是向缓冲区写入内容,然后读取所写入的内容。
“限制”标记了缓冲区中读取或写入操作应该停止的位置:它是第一个不应该被读取/写入的位置。
如果我们想向缓冲区写入内容,我们希望从开头开始,并在缓冲区满时停止;提前停止没有意义(除非我们没有数据可用)。因此,将“限制”设置为缓冲区的“容量”是有意义的。
因此,当我们想要开始向缓冲区写入内容时,我们调用“clear”。这将将当前“位置”设置为缓冲区的开头,并将“限制”设置为其容量。
如果我们想要从缓冲区开始读取,情况就不同了。我们不知道实际写入缓冲区的元素数量 - 我们不知道它是否已满。但通常我们只想读取与写入的元素数量相同的元素:超过这个点的任何内容都是垃圾;读取到缓冲区末尾可能是一个错误!
幸运的是,之前的写操作已经将最后的位置保留在原地,所以缓冲区本身知道写入了多少元素。它通过
flip
方法将
limit
设置为当前的
position
,然后将当前的
position
设置为 0,很好地封装了这个信息。
实际上,这意味着在调用了
flip
之后,当我们从缓冲区开始读取时,我们将从开头开始,并在读取到上一次写操作写入的最后一个元素后停止。这确保我们不会读取到垃圾数据。
rewind
的功能与
flip
类似,但又有明显的区别。它并不会将
limit
重置为旧的
position
。使用
rewind
通常在你有比仅仅写入和读取缓冲区更具体的用例时才有意义。
例如,如果你已经向缓冲区写入数据,调用了
flip
,读取了缓冲区,现在想再次读取它(第二次或第三次),那么你会使用
rewind
。
除非你有明确而具体的理由,否则不要使用rewind
。对于常见情况,即读取刚刚写入缓冲区的所有数据,请使用flip
代替。
将你的示例重写为更接近常见用例的形式:
ByteBuffer bb = ByteBuffer.allocate(2);
byte b = 127;
bb.put(b);
bb.rewind();
System.out.println("Reading buffer after rewinding:");
while (bb.hasRemaining()) {
System.out.println(bb.get());
}
ByteBuffer bbFlip = ByteBuffer.allocate(2);
bbFlip.put(b);
bbFlip.flip();
System.out.println("Reading buffer after flipping:");
while (bbFlip.hasRemaining()) {
System.out.println(bbFlip.get());
}
如图所示,当我们以这种方式使用
rewind
时,我们会丢失有关写入缓冲区的元素数量的信息。如果我们的要求是必须读取刚刚写入的那些(仅限于那些)元素,那么我们刚刚在我们的代码中引入了一个错误!
read()/put()
和write()/get()
?难道不应该是read()/get()
和write()/put()
吗?因为获取操作意味着读取,而放置则意味着写入。 - user963241read()
和write()
或者get()
和put()
操作的上下文,以涵盖所有可能性,这正是一个好答案应该做的,而后者与Channels
毫无关系。你正在违背现实。 - user207421Channel
的read
和write
方法,只是因为它们首先被提到,并且get
和put
方法在括号里面。我认为一个伟大的答案(我说过你的不是)应该强调ByteBuffer
本身的方法。但是这个答案还是可以的,只是不是“伟大”的,而且这只是我的观点,与“现实”没有太多关系。 - haelix