Java SE 1.4引入了大量用于改进输入/输出处理机制的特性,它们位于java.nio包中,合称“新I/O”。
这个包中包含对下列特性的支持:字符集编码器和解码器,非阻塞的I/O ,内存映射文件,文件加锁机制 。字符集编码器和解码器在前面已经说过了。
内存映射文件
大多数操作系统都可以利用虚拟内存实现来将一个文件或者文件的一部分“映射”到内存中。然后,这个文件就可以当作是内存数组一样地访问,这比传统的文件操作要快得多 。内存映射比使用带缓冲的顺序输入要稍微快一点,但是比使用RandomAccessFile快很多。
当然,精确的值因机器不同会产生很大的差异,但是很明显,如果需要使用随机访问,那么性能受损是不可避免的。另一方面,对于中等尺寸文件的顺序读入没有必要使用内存映射。
首先,从文件中获得一个通道(
channel),通道是用于磁盘文件的一种抽象,它使我们可以访问诸如内存映射、文件加锁机制以及文件间快速数据传递等操作系统特性。我们可以通过调用getChannel方法来获得通道,这个方法已经添加到了FileInputStream、FileOutputStream和RandomAccessFile类中。
FileInputStream f = new FileInputStream(xxx);
FileChannel chnanel = f.getChannel();
然后,通过调用FileChannel类的map方法从这个通道中获得一个MappedByteBuffer。你可以指定想要映射的文件区域与映射模
式,有三种模式可以得到支持:
FileChannel.MapMode.READ_ONLY:所产生的缓冲区是只读的,任何对该缓冲区写入的尝试都会导致ReadOnlyBufferException异常。
FileChannel.MapMode.READ_WRITE:所产生的缓冲区是可写的,任何修改都会在某个时刻写回到文件中。注意,其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的确切行为是依赖于操作系统的。
FileChannel.MapMode.PRIVATE:所产生的缓冲区是可写的,但是任何修改对这个缓冲区来说都是私有的,不会传播到文件中。一旦有了缓冲区,就可以使用ByteBuffer类和Buffer超类的方法读写数据了。
文件加锁机制
文件锁可以控制对文件或文件中某个范围的字节的访问,但是,文件加锁机制在不同的操作系统之间变化很大。
FileChannel
FileLock lock()
在整个文件上获得一个独占的锁,这个方法将阻塞直至获得锁。
FileLock tryLock()
在整个文件上获得一个独占的锁,或者在无法获得锁的情况下返回null。
FileLock lock(long position, long size, boolean shared)
FileLock tryLock(long position, long size, boolean shared)
在文件的一个区域上获得锁。第一个方法将阻塞直至获得锁,而第二个方法将在无法获得锁时返回null。
参数: position 要锁定区域的起始位置
size 要锁定区域的尺寸
shared true为共享锁, false为独占锁
FileLock
void release()
释放这个锁。
MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)将文件的一个区域映射到内存中。
参数: mode FileChannel.MapMode类中的常量READ_ONLY、 READ_WRITE、或PRIVATE之一
position 映射区域的起始位置
size 映射区域的大小
buffer
boolean hasRemaining()
如果当前的缓冲区位置没有达到这个缓冲区的界限位置则返回true。
int limit()
返回这个缓冲区的界限位置,即没有任何值可用的第一个位置。
ByteBuffer
byte get()从当前位置获得一个字节,并将当前位置推到下一个字节。
byte get(int index)从指定索引处获得一个字节。
ByteBuffer put(byte b)向当前位置推入一个字节,并将当前位置推到下一个字节。返回对这个缓冲区的引用。
ByteBuffer put(int index, byte b)向指定索引处推入一个字节。返回对这个缓冲区的引用。
ByteBuffer get(byte[] destination)
ByteBuffer get(byte[] destination, int offset, int length)
用缓冲区中的字节来填充字节数组,或者字节数组的某个区域,并将当前位置向前推读入的字节数个位置。如果缓冲区不够,那么就不会读入任何字节,并抛出BufferUnderflowException。返回对这个缓冲区的引用。
参数: destination 要填充的字节数组
offset 要填充区域的偏移量length 要填充区域的长度
ByteBuffer put(byte[] source)
ByteBuffer put(byte[] source, int offset, int length)
将字节数组中的所有字节或者给定区域的字节都推入缓冲区中,并将当前位置向前推写出的字节数个位置。如果缓冲区不够,那么就不会读入任何字节,并抛出BufferUnderflow Exception。返回对这个缓冲区的引用。
参数: source 要写出的数组
offset 要写出区域的偏移量
length 要写出区域的长度
Xxx getXxx()
Xxx getXxx(int index)
ByteBuffer putXxx(xxx value)
ByteBuffer putXxx(int index, xxx value)
获得或放置一个二进制数。 Xxx Int、 Long、 Short、 Char、 Float或Double中的一个。
ByteBuffer order(ByteOrder order)
ByteOrder order()
设置或获得字节顺序, order的值是ByteOrder类的常量BIG_ENDIAN或LITTLE_ENDIAN中
的一个
Buffer clear()通过将位置复位到0,并将界限复位到容量,使这个缓冲区为写出做好准备。返回this。
Buffer flip()通过将界限设置到位置,并将位置复位到0,使这个缓冲区为读入做好准备。返回this。
Buffer rewind()通过将读写位置复位到0,并保持极限不变,使这个缓冲区为重新读入相同的值做好准备。返回this。
Buffer mark()将这个缓冲区的标记设置到读写位置,返回this。
Buffer reset()将这个缓冲区的位置设置到标记,从而允许被标记的部分可以再次被读入或写出,返回this。
int remaining()返回剩余可读入或可写出的值的数量,即界限与位置之间的差异。
int position()返回这个缓冲区的位置。
int capacity()返回这个缓冲区的容量。
charbuffer
char get()
CharBuffer get(char[] destination)
CharBuffer get(char[] destination, int offset, int length)
从这个缓冲区的位置处开始,获得一个char值,或者某个范围的char值,然后将位置向前推过所读入的字符。最后两个方法返回this。
CharBuffer put(char c)
CharBuffer put(char[] source)
CharBuffer put(char[] source, int offset, int length)
CharBuffer put(String source)
CharBuffer put(CharBuffer source)
从这个缓冲区的位置处开始,推入一个char值,或者某个范围的char值,然后将位置向前推过所写出的字符。
CharBuffer read(CharBuffer destination)
从这个缓冲区中获得char值,然后将它们推入目标缓冲区,直至达到目标缓冲区的界限。返回this。