Java NIO (二)

在上一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道。本文为NIO入门学习的第二篇,将会分析NIO中的缓冲区Buffer的内部原理。

在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如果我们使用get()方法从缓冲区获取数据或者使用put()方法把数据写入缓冲区,都会引起缓冲区状态的变化。

在缓冲区中,最重要的属性有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪:

position

指定了下一个将要被写入或者读取的元素索引。在从Channel读取数据到Buffer时,position变量用来跟踪截止目前为止从Channel中读出了多少数据,在从Buffer中向Channel写数据时,position变量用来跟踪截止目前为止向Channel写入了多少数据。

limit

在从Channel中读取数据到Buffer中时,limit变量指示了还剩多少空间可供存放数据,在从Buffer向Channel写数据时,limit变量指示了还剩多少数据可以写入。position正常情况下小于或者等于limit。

capacity

指示Buffer最多能够存储的数据。实际上,它指示了底层array的容量,或者至少是底层array允许使用的空间数量。Limit永远不会大于capacity。

 

接下来我们将逐一检查每个细节,并且也看看为什么这样的设计适合典型的读/写(输入/输出)处理。我们假设从一个Channel拷贝数据到另一个Channel。

首先新建一个容量大小为10的ByteBuffer对象,在初始化的时候,position设置为0,如果我们读一些数据到缓冲区中,那么下一个读取的数据就进入索引为0的字节。如果我们从缓冲区写一些数据,从缓冲区读取的下一个字节就来自索引为0的字节。limit和 capacity被设置为10,在以后使用ByteBuffer对象过程中,capacity的值不会再发生变化,而其它两个将会随着使用而变化。

 

现在我们可以从读通道中读取一些数据到缓冲区中。如果读取到4个字节数据,则此时position的值为4,即下一个将要被写入的字节索引为4,而limit仍然是10,如下图所示:

 

下一步把读取到的数据写入到写通道中,在此之前必须调用flip()方法,该方法将会完成两件事情:

1. 把limit设置为当前的position值

2. 把position设置为0

position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多,如下图所示:

 

我们现在可以将数据从缓冲区写入通道了,这会导致position的增加而limit保持不变,但position不会超过limit的值,所以在读取我们之前写入到缓冲区中的4个字节之后,position和limit的值都为4,如下图所示:

 

在数据写入到写通道完毕后,调用clear()方法能够把所有的状态变化设置为初始化时的值,该方法将会完成两件事情:

1. 把limit设置为capacity值

2. 把position设置为0

clear()方法会重置Buffer以便接收更多的字节,如下图所示:

 

最后我们用一段代码来验证这个过程,如下所示:

[java] view plain copy

  1. import java.io.FileInputStream;  
  2. import java.io.FileOutputStream;  
  3. import java.nio.Buffer;  
  4. import java.nio.ByteBuffer;  
  5. import java.nio.channels.FileChannel;  
  6.   
  7. public class TestBufferField {  
  8.   
  9.     public static void main(String[] args) throws Exception {  
  10.         FileInputStream fileInStream = new FileInputStream("D:\\test1.txt");  
  11.         // 获取读通道  
  12.         FileChannel fcin = fileInStream.getChannel();  
  13.   
  14.         FileOutputStream fileOutStream = new FileOutputStream("D:\\test2.txt");  
  15.         // 获取写通道  
  16.         FileChannel fcout = fileOutStream.getChannel();  
  17.   
  18.         // 创建缓冲区  
  19.         ByteBuffer buffer = ByteBuffer.allocate(10);  
  20.         output("初始化", buffer);  
  21.   
  22.         // 从通道fcin读取数据到缓冲区  
  23.         fcin.read(buffer);  
  24.         output("调用read()", buffer);  
  25.   
  26.         // 重设buffer,将limit设置为position,然后将position设置为0  
  27.         buffer.flip();  
  28.         output("调用flip()", buffer);  
  29.   
  30.         // 将缓冲区中的数据写入通道fcout  
  31.         fcout.write(buffer);  
  32.         output("调用write()", buffer);  
  33.   
  34.         // 重设buffer,将limit设置为容量capacity,position设置为0  
  35.         buffer.clear();  
  36.         output("调用clear()", buffer);  
  37.   
  38.         fileInStream.close();  
  39.         fileOutStream.close();  
  40.     }  
  41.   
  42.     public static void output(String step, Buffer buffer) {  
  43.         System.out.println(step + " : ");  
  44.         System.out.print("position: " + buffer.position() + ", ");  
  45.         System.out.print("limit: " + buffer.limit() + ", ");  
  46.         System.out.println("capacity: " + buffer.capacity());  
  47.         System.out.println();  
  48.     }  
  49.   
  50. }  

输出结果为:

这与我们上面演示的过程一致。在后面的文章中,我们继续介绍NIO中关于缓冲区一些更高级的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值