J2SE1.4的I/O新特性译者序:这是一篇比较使用的I/O新特性的介绍文章。文中使用了大量的代码实例来演示和解说如何使用J2SE1.4的新I/O特性并提供你应用程序的性能,而且提供了两个完整的例子,其中包括一个循环WEB服务器的雏形,非常值得我们参考。 回溯到2000年的1月,当人们正在争论着公元2000年究竟是一个世纪的开始还是一个实际的结束的时候,一份新的JAVA规范——JSR(Java Specification Request)51也被审核通过了。这份JSR的名字是《New I/O APIs for the Java Platform》(JAVA平台的新I/O API)。许多人认为这份新的规范只会给大家带来非阻塞I/O操作的能力,但是在JSDK1.4Beta(JavaTM 2 Platform, Standard Edition)中引入的新的特性,却还包含其它的一些新而有趣的特征。新的API在提供了可升级的套接口(socket)和文件I/O操作的同时(这是理所当然的),你也可以找到一个正则表达的包来支持模式匹配,以及对字符集转换的编码器和解码器,和优化过的文件系统支持如文件锁定、内存映射等功能。我们在这篇文章中的讨论会全面覆盖上面所说的四个新特性。 注意:JAVA本地接口(JNI)为新的I/O操作所做的修改我们将不会涉及,如果你需要了解有关的内容,请参考本文结尾“资源”部分的有关内容。 Buffers 按照从最简单到最复杂的习惯,我们将从java.nio包中的一系列Buffer类开始说起。Buffer提供了一种在内存容器中保存一系列原始数据的机制。基本上,你可以设想一下,把DataInputStream/DataOutputStream组合在一起封装成一个固定字节大小的数组而只允许读写一种数据类型,例如char,int,或者double。在这个包里,总共有7种这样的Buffer可用: · ByteBuffer · CharBuffer · DoubleBuffer · FloatBuffer · IntBuffer · LongBuffer · ShortBuffer 实际上,ByteBuffer也能够对其它六种类型进行读写,但是这些特别的Buffer更有针对性,更专门化一些。为了示范如何使用一个Buffer,接下来这一小片代码将完成一个从String型变量到一个CharBuffer的转换,并从这个Buffer中逐一的读出单个字符。你可以用warp方法来完成转换,用get方法来取一个字符。
在使用Buffer的时候,一定要注意它目前的大小(sizing)和位置(positioning)的值是有区别的,千万不要混淆了。方法length是不规范的,尤其是对于CharBuffer而言。当然这并非是出了什么错,而是它返回的是Buffer中的剩余长度的值,所以如果position并非在Buffer的开始处的话,返回值将不是Buffer的长度,而是在Buffer中剩余的字符的长度。换句话说,上面程序中的循环也可以修改成这样:
我们回到正题,继续讨论大小(sizing)和位置(positioning)的关系,在这里,有四个概念必须明确,它们是mark(标记),position(位置),limit(限制),和capacity(容量)。 · mark——用mark方法设置的可设位置,mark方法可以使用reset来重置position,<=position,>=0; · position——在Buffer中目前读写的位置,<=limit; · limit——第一个不应该被读取的元素的位置的index(索引号),<=cpacity; · capcity——Buffer的大小,>=size。 Position(位置)属性值是我们在对一个Buffer读取或者写入的时候需要时刻牢记的信息。例如,如果你想读取你刚刚写入的字符,你不许把position移动到你想读取的位置,否则,你将越过limit的限制,而读到一个不知道是什么的字符。这时候你需要立刻使用flip方法,把limit移动到当前的位置,并把position移动到0位置。你也可以回绕一个buffer来保持当前的limit位置,而把position返回到0位置。举个例子,如果从下面这一小段代码中的flip调用去掉,将返回一个空白,因为在buffer中还什么都没有。
上面的封装机制是一个非直接缓冲(non-direct buffer)的例子。非直接缓冲也可以通过allocate方法来创建和限定大小,本质上来说,只是把数据封装到一个数组里了。如果愿意消耗稍微多一点的创建资源,你也可以通过allocateDirect方法开辟一块连续的内存来保存数据,这也可以称作直接缓冲。直接缓冲是依赖于系统的本地接口的I/O操作来优化存取操作的。 文件映射 MappedByteBuffer是一个专门用于直接缓冲的ByteBuffer,这个类用字节缓冲来映射一个文件。想要映射一个文件到MappedByteBuffer,你必须先取得这个文件的通道(channel)。通道是某种已建立的连接,例如管道(pipe),套接口(socket),或者文件(file)等能够完成I/O操作的对象。如果是文件通道,你可以通过 FileInputStream(文件输入流),FileOutputStream(文件输出流)或者RandomAccessFile(随机存取文件)的getChannel方法来获得一个通道。一旦你取得这个通道,你就可以通过它的map方法,指明映射模式来把你想映射的那一部分文件映射到缓冲中去。文件通道可以使用FileChannel.MapMode的任一个常数打开:只读(READ_ONLY),私有/写时拷贝(PRIVATE),或者读写(READ_WRITE)。 下面是一个从文件中创建只读的MappedByteBuffer的基本例程:
|