分享

java中NIO库的应用

 java_laq小馆 2013-07-27

java中NIO库的应用
分类: java 2009-04-01 10:30 628人阅读 评论(1) 收藏 举报
javabufferbyteexceptionimportcharacter
 
    新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。它弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向

块 的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用低级优化,这是原来的

I/O 包所无法做到的。NIO 与原来的 I/O 有同样的作用和目的,但是块 I/O 的效率可以比流 I/O 高许多。NIO 将最耗时的 I/O 操

作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。

    通道和缓冲区是NIO中的核心对象,几乎在每一个 I/O 操作中都要使用它们。到任何目的地(或来自任何地方)的所有数据都必须

通过一个 Channel 对象。
    一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据

都要读到缓冲区中。

缓冲区:

Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。

在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。

在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任

何时候访问NIO中的数据,您都是将它放到缓冲区中。

缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区

提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

对于每一种基本 Java 类型都有一种缓冲区类型:ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer

DoubleBuffer

每一个 Buffer 类都是 Buffer 接口的一个实例。除了 ByteBuffer,每一个 Buffer 类都有完全一样的操作,只是它们所处理的数

据类型不一样。因为大多数标准 I/O 操作都使用 ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作。

 

通道:

Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。

正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多

个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而

通道 可以用于读、写或者同时用于读写。

从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据

填充它,然后让通道用这些数据来执行写入操作。

 

读取文件涉及三个步骤:(1) 从 FileInputStream 获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel 读到 Buffer 中:
import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class ReadAndShow {
static public void main( String args[] ) throws Exception {
    /* 获取通道 */
    FileInputStream fin = new FileInputStream( "readandshow.txt" );
    FileChannel fc = fin.getChannel();
        /* 创建缓冲区 */
    ByteBuffer buffer = ByteBuffer.allocate( 1024 );
        /* 将数据从通道读到缓冲区中 */
    fc.read( buffer );
        /* 下面的方法后面再讲 */
    buffer.flip();

    int i=0;
    while (buffer.remaining()>0) {
      byte b = buffer.get();
      System.out.println( "Character "+i+": "+((char)b) );
      i++;
    }

    fin.close();
}
}

 

写入文件:
public class WriteSomeBytes {
static private final byte message[] = { 83, 111, 109, 101, 32,
                                          98, 121, 116, 101, 115, 46 };

static public void main( String args[] ) throws Exception {
    FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
    FileChannel fc = fout.getChannel();
    ByteBuffer buffer = ByteBuffer.allocate( 1024 );
   
    for (int i=0; i<message.length; ++i) {
      buffer.put( message[i] );
    }

    buffer.flip();

    fc.write( buffer );

    fout.close();
}
}
在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。

重设缓冲区:

buffer.clear();
int r = fcin.read( buffer );

if (r==-1) {
     break;
}

buffer.flip();
fcout.write( buffer );
clear() 方法重设缓冲区,使它可以接受读入的数据。 flip() 方法让缓冲区可以将新读入的数据写入另一个通道。

 

状态变量:

position limit capacity

这三个变量一起可以跟踪缓冲区的状态和它所包含的数据。

在从通道读取时,将所读取的数据放到底层的数组中。

position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果从通道中读三个

字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。

在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组

的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。

limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。

position 总是小于或者等于 limit。

缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用

的底层数组的容量。limit 决不能大于 capacity。

flip() 方法做两件非常重要的事:
1. 它将 limit 设置为当前 position。
2. 它将 position 设置为 0。

clear() 方法重设缓冲区以便接收更多的字节。 Clear 做两种非常重要的事情:
1. 它将 limit 设置为与 capacity 相同。
2. 它设置 position 为 0。

get() 方法

ByteBuffer 类中有四个 get() 方法:
   1. byte get();
   2. ByteBuffer get(byte dst[]);
   3. ByteBuffer get(byte dst[], int offset, int length);
   4. byte get(int index);
第一个方法获取单个字节。第二和第三个方法将一组字节读到一个数组中。第四个方法从缓冲区中的特定位置获取字节。那些返回

ByteBuffer 的方法只是返回调用它们的缓冲区的 this 值。

前三个 get() 方法是相对的,而最后一个方法是绝对的。 相对 意味着 get() 操作服从 limit 和 position 值 — 更明确地说,

字节是从当前 position 读取的,而 position 在 get 之后会增加。另一方面,一个 绝对 方法会忽略 limit 和 position 值,也

不会影响它们。事实上,它完全绕过了缓冲区的统计方法。

上面列出的方法对应于 ByteBuffer 类。其他类有等价的 get() 方法,这些方法除了不是处理字节外,其它方面是是完全一样的,

它们处理的是与该缓冲区类相适应的类型。

put()方法

ByteBuffer 类中有五个 put() 方法:
   1. ByteBuffer put(byte b);
   2. ByteBuffer put(byte src[]);
   3. ByteBuffer put(byte src[], int offset, int length);
   4. ByteBuffer put(ByteBuffer src);
   5. ByteBuffer put(int index, byte b);
第一个方法 写入(put) 单个字节。第二和第三个方法写入来自一个数组的一组字节。第四个方法将数据从一个给定的源

ByteBuffer 写入这个 ByteBuffer。第五个方法将字节写入缓冲区中特定的 位置 。那些返回 ByteBuffer 的方法只是返回调用它们

的缓冲区的 this 值。

与 get() 方法一样,把 put() 方法划分为 相对 或者 绝对 的。前四个方法是相对的,而第五个方法是绝对的。

上面显示的方法对应于 ByteBuffer 类。其他类有等价的 put() 方法,这些方法除了不是处理字节之外,其它方面是完全一样的。

它们处理的是与该缓冲区类相适应的类型。

ByteBuffer 还有用于读写不同类型的值的其他方法:
getByte() getChar() getShort() getInt() getLong() getFloat() getDouble()
putByte() putChar() putShort() putInt() putLong() putFloat() putDouble()

这其中的每个方法都有两种类型 — 一种是相对的,另一种是绝对的。
###############################################################################################

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多