Java IO教程 - Java I/O缓冲区
什么是NIO?
在NIO中,我们处理I/O操作的通道和缓冲区。
像流一样的通道表示数据源/接收器和用于数据传输的Java程序之间的连接。
通道提供双向数据传输设施。我们可以使用通道来读取数据以及写入数据。根据我们的需要,我们可以获得只读通道,只写通道或读写通道。
在基于流的I/O中,数据传输的基本单位是一个字节。在基于通道的NIO中,数据传输的基本单位是缓冲器。
缓冲区具有确定其可以包含的数据的上限的固定容量。
在基于通道的I/O中,我们将数据写入缓冲区,并将该缓冲区传递到写入数据的通道。
为了从数据源读取数据,我们向一个通道传递一个缓冲区。通道将数据从数据源读入缓冲区。
缓冲区
缓冲区是固定长度的数据容器。有一个单独的缓冲区类型来保存每种类型的基本值的数据,除了布尔类型值。
缓冲区是我们程序中的一个对象。我们有一个单独的类来表示每种类型的缓冲区。
所有缓冲区类都继承自一个抽象的Buffer类。包含原始值的缓冲区类如下:
- ByteBuffer
- ShortBuffer
- CharBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
不同的缓冲区保存不同数据类型的数据。例如,ByteBuffer保存字节值; ShortBuffer保存短值;一个CharBuffer保存字符,等等。
缓冲区属性
以下是缓冲区的四个重要属性。
- Capacity
- Position
- Limit
- Mark
缓冲区的容量是它可以容纳的元素的最大数量。并且当创建缓冲器时它是固定的。
我们可以通过调用hasArray()方法检查缓冲区是否由数组支持,如果缓冲区由数组支持则返回true。
我们可以通过使用缓冲对象的array()方法来访问支持数组。
一旦我们访问了后台数组,对该数组所做的任何更改都将反映在缓冲区中。
缓冲区具有返回其容量的capacity()方法。
缓冲区创建
我们可以使用特定缓冲区类的allocate()工厂方法创建一个缓冲区,如下所示:
以下代码创建一个容量为8的字节缓冲区
ByteBuffer bb = ByteBuffer.allocate(8);
获得容量
int capacity = bb.capacity();
以下代码创建一个容量为1024的字符缓冲区
CharBuffer cb = CharBuffer.allocate(1024);
字节缓冲区有一个名为allocateDirect()的方法,它创建一个字节缓冲区,从操作系统内存中分配内存,而不是从JVM堆中分配内存。
我们可以使用ByteBuffer类的isDirect()方法来检查缓冲区是直接还是非直接。
// Create a direct byte buffer of 512 bytes capacity ByteBuffer bbd = ByteBuffer.allocateDirect(512);
另一种创建缓冲区的方法是使用缓冲区的static wrap()方法包装数组。
byte[] byteArray = new byte[512]; ByteBuffer bb = ByteBuffer.wrap(byteArray);
我们可以使用相同的技术来创建缓冲区来存储其他原始值。
当我们创建一个缓冲区时,缓冲区的所有元素都被初始化为零值。
缓冲区索引位置
缓冲区的每个元素都有一个索引。第一个元素的索引为0,最后一个元素的索引为capacity-1。
创建缓冲区时,其位置设置为0,其限制等于其容量。
我们可以使用它的重载position()方法获取/设置缓冲区的位置。
position()方法返回缓冲区位置的当前值。
position(int newPosition)方法将缓冲区的位置设置为指定的newPosition值,并返回缓冲区的引用。
我们可以使用它的重载limit()方法获取/设置缓冲区的限制。
limit()方法返回缓冲区限制的当前值。limit(int newLimit)方法将缓冲区的限制设置为指定的newLimit值,并返回缓冲区的引用。
我们可以使用mark()方法为缓冲区的位置添加书签。当我们调用mark()方法时,缓冲区将其位置的当前值存储为其标记值。我们可以通过使用reset()方法将缓冲区的位置设置为之前加书签的值。
缓冲区的标记在创建时未定义。只有当定义了它的标记时,我们才必须在缓冲区上调用reset()方法。否则,reset()方法会抛出InvalidMarkException异常。
以下代码创建一个新缓冲区并显示其四个属性。
import java.nio.ByteBuffer; import java.nio.InvalidMarkException; public class Main { public static void main(String[] args) { ByteBuffer bb = ByteBuffer.allocate(8); System.out.println("Capacity: " + bb.capacity()); System.out.println("Limit: " + bb.limit()); System.out.println("Position: " + bb.position()); // The mark is not set for a new buffer. Calling the // reset() method throws a runtime exception if the mark is not set. try { bb.reset(); System.out.println("Mark: " + bb.position()); } catch (InvalidMarkException e) { System.out.println("Mark is not set"); } } }
上面的代码生成以下结果。