java中的Buffer抽象类

随时随地阅读更多技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666)

一、概述

Buffer是一个抽象类,位于java.nio包下,类声明如下:public abstract classBufferextends Object

Buffer是一个用于特定基本类型数据的容器。

缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量、限制和位置:

缓冲区的容量 是它所包含的元素的数量。缓冲区的容量不能为负并且不能更改。

缓冲区的限制 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。

缓冲区的位置 是下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。

对于每个非 boolean 基本类型,此类都有一个子类与之对应。

传输数据

此类的每个子类都定义了两种获取放置 操作: 

相对 操作读取或写入一个或多个元素,它从当前位置开始,然后将位置增加所传输的元素数。如果请求的传输超出限制,则相对获取 操作将抛出BufferUnderflowException,相对放置 操作将抛出BufferOverflowException;这两种情况下,都没有数据被传输。  

绝对 操作采用显式元素索引,该操作不影响位置。如果索引参数超出限制,绝对获取 操作和放置 操作将抛出 IndexOutOfBoundsException

当然,通过适当通道的 I/O 操作(通常与当前位置有关)也可以将数据传输到缓冲区或从缓冲区传出数据。 

做标记和重置 

缓冲区的标记 是一个索引,在调用 reset 方法时会将缓冲区的位置重置为该索引。并非总是需要定义标记,但在定义标记时,不能将其定义为负数,并且不能让它大于位置。如果定义了标记,则在将位置或限制调整为小于该标记的值时,该标记将被丢弃。如果未定义标记,那么调用reset 方法将导致抛出InvalidMarkException。  

不变式 

位置、限制和容量值遵守以下不变式:0 <= 标记 <= 位置 <=限制<= 容量

新创建的缓冲区总有一个 0 位置和一个未定义的标记。初始限制可以为 0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。

清除、反转和重绕

除了访问位置、限制、容量值的方法以及做标记和重置的方法外,此类还定义了以下可对缓冲区进行的操作:

  • clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。

  • flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。

  • rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。

只读缓冲区 

每个缓冲区都是可读取的,但并非每个缓冲区都是可写入的。每个缓冲区类的转变方法都被指定为可选操作,当对只读缓冲区调用时,将抛出 ReadOnlyBufferException。只读缓冲区不允许更改其内容,但其标记、位置和限制值是可变的。可以调用其isReadOnly 方法确定缓冲区是否为只读。

线程安全

多个当前线程使用缓冲区是不安全的。如果一个缓冲区由不止一个线程使用,则应该通过适当的同步来控制对该缓冲区的访问。

调用链

指定此类中的方法返回调用它们的缓冲区(否则它们不会返回任何值)。此操作允许将方法调用组成一个链;例如,语句序列

b.flip();
 b.position(23);
 b.limit(42);

可以由以下更紧凑的一个语句代替 

b.flip().position(23).limit(42);

二、方法

1、public final intcapacity()    返回此缓冲区的容量。

2、public final int position()  返回此缓冲区的位置。

3、public finalBufferposition(int newPosition)   设置此缓冲区的位置。如果标记已定义且大于新的位置,则丢弃该标记。

参数:newPosition - 新位置值;必须为非负且不大于当前限制

返回:此缓冲区

抛出:IllegalArgumentException - 如果newPosition 不满足先决条件

4、public final intlimit()  返回此缓冲区的限制。

5、public finalBufferlimit(int newLimit)   设置此缓冲区的限制。如果位置大于新的限制,则将它设置为新限制。如果标记已定义且大于新限制,则丢弃该标记。

参数:newLimit - 新限制值;必须为非负且不大于此缓冲区的容量

返回:此缓冲区

抛出:IllegalArgumentException - 如果newLimit 不满足先决条件

6、public finalBuffermark()   在此缓冲区的位置设置标记。

7、public finalBufferreset()  将此缓冲区的位置重置为以前标记的位置。调用此方法不更改也不丢弃标记的值。

返回:此缓冲区

抛出:InvalidMarkException - 如果尚未设置标记

8、public final Buffer clear()  清除此缓冲区。将位置设置为 0,将限制设置为容量,并丢弃标记。

在使用一系列通道读取或放置 操作填充此缓冲区之前调用此方法。例如:

 buf.clear();     // Prepare buffer for reading
 in.read(buf);    // Read data

此方法不能实际清除缓冲区中的数据,但从名称来看它似乎能够这样做,这样命名是因为它多数情况下确实是在清除数据时使用。

9、public final Buffer flip()

反转此缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。在一系列通道读取或放置 操作之后,调用此方法为一系列通道写入或相对获取 操作做好准备。例如:

   buf.put(magic); // Prepend header

   in.read(buf); // Read data into rest of buffer

   buf.flip(); // Flip buffer

    out.write(buf); // Write header + data to channel

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

10、public final int remaining()  返回当前位置与限制之间的元素数。

11、public final boolean hasRemaining()  告知在当前位置和限制之间是否有元素。

12、public abstract boolean isReadOnly() 告知此缓冲区是否为只读缓冲区。

13、public abstract booleanhasArray()  告知此缓冲区是否具有可访问的底层实现数组。如果此方法返回true,则可以安全地调用 arrayarrayOffset 方法。

14、public abstractObject array()  返回此缓冲区的底层实现数组(可选操作)。此方法旨在使具有底层实现数组的缓冲区能更有效地传递给本机代码。具体子类为此方法提供更强类型的返回值。对此缓冲区内容进行修改将导致返回数组的内容被修改,反之亦然。调用此方法前应调用hasArray 方法,以确保此缓冲区存在可访问的底层实现数组。

返回:此缓冲区的底层实现数组

抛出:ReadOnlyBufferException - 如果此缓冲区存在底层实现数组,但它是只读的

          UnsupportedOperationException - 如果此缓冲区不存在可访问的底层实现数组

15、public abstract intarrayOffset()  

返回此缓冲区的底层实现数组中第一个缓冲区元素的偏移量(可选操作)

如果此缓冲区存在底层实现数组,那么缓冲区位置 p 对应于数组索引 p + arrayOffset()。

调用此方法前应调用 hasArray 方法,以确保此缓冲区存在可访问的底层实现数组。

返回:

此缓冲区数组中第一个缓冲区元素的偏移量

抛出:

ReadOnlyBufferException - 如果此缓冲区存在底层实现数组,但它是只读的

UnsupportedOperationException - 如果此缓冲区不存在可访问的底层实现数组

16、public abstract boolean isDirect()  告知此缓冲区是否为直接缓冲区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个非常经典的 Java NIO 相关问题,Buffer 类是 Java NIO 系统的一个核心类,它是一个抽象类,定义了许多抽象方法和一些常用的方法。在实际开发过程,我们经常会使用到 Buffer 相关的 API,其包括三个常用的函数:slice()、duplicate() 和 wrap()。 - slice():创建一个与原始缓冲区共享数据的新缓冲区,可以理解成是原始缓冲区的一个视图,二者共享数据。新缓冲区的容量是原始缓冲区的剩余元素数量,位置是原始缓冲区当前位置,限制是原始缓冲区的剩余元素数量,标记被忽略。这个方法的主要作用就是将原始缓冲区的一个子区域作为一个新的缓冲区来使用,这个新的缓冲区与原始缓冲区数据共享,所以对新缓冲区的操作也会对原始缓冲区产生影响。 示例代码: ``` ByteBuffer buffer = ByteBuffer.allocate(10); for (int i = 0; i < buffer.capacity(); i++) { buffer.put((byte) i); } buffer.position(2); buffer.limit(6); ByteBuffer sliceBuffer = buffer.slice(); for (int i = 0; i < sliceBuffer.capacity(); i++) { byte b = sliceBuffer.get(i); b *= 11; sliceBuffer.put(i, b); } buffer.position(0); buffer.limit(buffer.capacity()); while (buffer.hasRemaining()) { System.out.println(buffer.get()); } ``` 输出结果为: ``` 0 1 22 33 44 5 6 7 8 9 ``` - duplicate():创建一个原始缓冲区的副本,这个副本与原始缓冲区共享数据,但是它拥有自己的索引、标记、限制和容量等属性,对副本的修改不会影响到原始缓冲区。这个方法的主要作用就是复制一个原始缓冲区,用于多线程操作或者备份。 示例代码: ``` ByteBuffer buffer = ByteBuffer.allocate(10); for (int i = 0; i < buffer.capacity(); i++) { buffer.put((byte) i); } ByteBuffer duplicateBuffer = buffer.duplicate(); for (int i = 0; i < duplicateBuffer.capacity(); i++) { byte b = duplicateBuffer.get(i); b *= 11; duplicateBuffer.put(i, b); } buffer.position(0); buffer.limit(buffer.capacity()); while (buffer.hasRemaining()) { System.out.println(buffer.get()); } ``` 输出结果为: ``` 0 1 2 3 4 5 6 7 8 9 ``` - wrap():创建一个包装了给定数组的缓冲区,这个缓冲区的容量是给定数组的长度,位置为 0,限制为数组的长度,标记被忽略。这个方法的主要作用就是将一个数组包装成缓冲区,从而可以使用缓冲区提供的方法对数组进行操作。 示例代码: ``` byte[] bytes = new byte[10]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) i; } ByteBuffer buffer = ByteBuffer.wrap(bytes); for (int i = 0; i < buffer.capacity(); i++) { byte b = buffer.get(i); b *= 11; buffer.put(i, b); } for (byte b : bytes) { System.out.println(b); } ``` 输出结果为: ``` 0 11 22 33 44 55 66 77 88 99 ``` 以上就是 slice()、duplicate() 和 wrap() 函数的作用和区别,以及示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值