java 打字机线程_java ByteBuffer flip()和limit()的理解, 转载的, 从里面理解到flip()的作用, 想象一下 老式打字机...

本文通过老式打字机的比喻解释了Java中的ByteBuffer如何使用flip()和limit()方法进行数据操作。通过示例代码展示了如何从一个文件复制数据到另一个文件,重点解析了flip()方法如何反转缓冲区的当前位置和限制,以及在数据读写过程中的作用。同时,文章还探讨了ByteBuffer的工作原理,包括put和get操作的影响以及如何避免BufferUnderflowException。
摘要由CSDN通过智能技术生成

先列点代码片段:

// ...

//

// 此段代码功能为从 t.txt 里复制所有数据到 out_j.txt:

//

...

1 FileChannel fcin = new FileInputStream( "d:/t.txt" ).getChannel();

2 FileChannel fcout = new FileOutputStream( new File( "d:/out_j.txt" )).getChannel();

3 ByteBuffer buff = ByteBuffer.allocate( 1024 );

4 long t1 = System.currentTimeMillis();

5

6 while( fcin.read( buff ) != -1 )

7 {

8     buff.flip();

9     fcout.write( buff );

10   buff.clear();

11 }

12

13 long t2 = System.currentTimeMillis();

14 long size = fcin.size();

15 javax.swing.JOptionPane.showMessageDialog( null, size + " 字节, 耗时 " + ( t2 - t1 + 1 ) + " ms." );

...

----------------------------------------------------------------------------------------------------

SDK 文档里对 ByteBuffer 的说明为:

public abstract class ByteBuffer

extends Buffer

implements Comparable

这说明 ByteBuffer 是继承于 Buffer 的抽象类, 实现了两个接口.

行3 通过 allocate() 分配了一块 1024 字节的缓冲区, 并返回一个 ByteBuffer 对象. (抽象类不能直接 new)

行6 fcin.read() 将数据读入到 buff. 此处的 read() 是 FileChannel 类的一个虚函数.

行8 buff.flip() 这个调用就是开头一直无法理解的部分.

----------------------------------------------------------------------------------------------------

SDK 文档里的对 flip() 的说明是:

public final Buffer flip()

反转 此缓冲区。首先对 当前位置 设置 限制 ,然后将该位置设置为零。如果已定义了标记,则丢弃该标记。

当将数据从一个地方传输到另一个地方时,经常将此方法与 compact 方法一起使用。

我最终的理解是: 文档翻译得太差了, 把不应该翻译的内容也译成了中文, 所以反而不容易理解.

关键就在以下 2 处:

当前位置 : 这个可以直观地理解为缓冲区中的当前数据指针, 或是  SQL 中的游标, 记为 curPointer.

限制 : 这个可以理解成实际操作的缓冲区段的结束标记, 记为 endPointer.

反转 : 这个完全是对 flip 这个词不负责的翻译, 如果参照 DirectX 里的 flip() 而译为翻转/翻页, 那就好理解得多, 就像写信/看信, 写/看完一页后, 翻到下一页, 眼睛/笔从页底重新移回页首.

这个翻转背后的操作其实就是 "把 endPointer 定位到 curPointer 处, 并把 curPointer 设为 0".

关于标记, 在这里不涉及. 下一句说到常与 compact 方法一起使用, 是可以想像的, 因为 compact 方法对数据进

行了压缩, 有效数据的真实长度发生了变化, 肯定需要用 flip 重新定位结束标记.

在填充, 压缩等数据操作时, curPointer 估计都是自动更新了位置的, 总是指向最后一个有效数据, 所以每次调

用 flip() 后, endPointer 就指向了有效数据的结尾, 而 curPointer 指向了 0 (缓冲起始处).

举个图例:

(c 和 e 分别代表 curPointer 和 endPointer 两个指针)

* 先是一个空的 ByteBuffer (大小为 10 字节)

-------------------

-------------------

c

e

* 然后填充 5 字节数据

-------------------

0 1 2 3 4

-------------------

e          c

此时, endPointer 尚在 0 处, curPointer 移到了数据结尾.

经测试, 此时若取数据, 将得到 5 个字节, 内容通常为 0 (也有可能是未知), 因为实际上取到的是从 c 处到缓冲

区实际结束处的 5 个未初始化的字节.

* 调用一次 flip() 后

-------------------

0 1 2 3 4

-------------------

c          e

此时, endPointer 先被移到 curPointer, 然后 curPointer 移到 0.

通过测试可见, ByteBuffer 取数据时, 是从 curPointer 起, 到 endPointer 止, 若 curPointer > endPointer, 则取到缓冲区结束.

再看上面代码的关键片段, 行 8 处调用 flip() 即有两个作用, 一是将 curPointer 移到 0, 二是将 endPointer 移到有效数据结尾.

此行可由以下两行代替:

buff.limit( buff.position());

buff.position( 0 );

可见对其工作原理的理解, 应该是正确的.

---------------------------- ------------------------------------------------------------------------

总结如下:

1. put 数据时, 不会自动清除缓冲区中现有的数据.

2. 每一次 get 或 put 后, curPointer 都将向缓冲区尾部移动, 移动量=操作的数据量.

3. get/put 均是从 curPointer 起, 到 curPointer + 操作的数据长度止.

4. get/put 操作中, 若 curPointer 超过了 endPointer 或缓冲区总长度, 将抛出 java.nio.BufferUnderflowException 异常.

注: curPointer 和 endPointer 只是为文中方便描述命名的, 实际分别对应到 ByteBuffer.position() 和 ByteBuffer.limit() 两个方法.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值