缓冲区基础
抽象类Buffer是java.nio包支持缓冲区的基础。 Buffer 的工作方式就象内存中用于读写基本数据类型的 RandomAccessFile 。象 RandomAccessFile 一样,使用 Buffer ,所执行的下一个操作(读/写)在当前某个位置发生。执行读/写操作中的任一个都会改变那个位置,所以在写操作之后进行读操作不会读到刚才所写的内容,而会读到刚才所写内容之后的数据。 Buffer 提供了四个指示方法,用于访问线性结构(从最高值到最低值):
capacity() :表明缓冲区的容量大小, 一旦确定了大小, 将不能再改变;limit() :告诉您到目前为止已经往缓冲区填了多少字节,或者让您用 :limit(int newLimit) 来改变这个限制 position() :告诉您当前的位置,以执行下一个读/写操作 mark() :为了稍后用 reset() 进行重新设置而记住某个位置 flip() :交换限制指针和位置指针,然后将位置置为 0,并废弃已经做的mark标记缓冲区的基本操作是读 get() 和写 put() ;然而,这些方法在子类中都是针对每种数据类型的特定方法。为了说明这一情况,让我们研究一个简单示例,该示例演示了从同一个缓冲区读和写一个字符。在清单 1 中, flip() 方法交换限制和位置,然后将位置置为 0,并废弃标记,让您读刚才所写的数据:
清单 1. 读/写示例import java.nio.*;...CharBuffer buff = ...;buff.put('A');buff.flip();char c = buff.get();System.out.println("An A: " + c);
现在让我们研究一些具体的 Buffer 子类。
缓冲区类型
Merlin 具有 7 种特定的 Buffer 类型,每种类型对应着一个基本数据类型(不包括 boolean):
ByteBuffer //存放任何除boolean类型外的其他基本类型CharBuffer //存放charDoubleBuffer //存放doubleFloatBuffer //存放floatIntBuffer //存放intLongBuffer //存放longShortBuffer //存放short在本文后面,我将讨论第 8 种类型 MappedByteBuffer ,它用于内存映射文件。如果您必须使用的类型不是这些基本类型,则可以先从 ByteBuffer 获得字节类型,然后将其转换成 Object 或其它任何类型。 创建缓冲区一共有两种类型的缓冲区,直接缓冲区和非直接缓冲区。在创建缓冲区时,可以要求创建直接缓冲区,创建直接缓冲区的成本要比创建间接缓冲区高,但这可以使运行时环境直接在该缓冲区上进行较快的本机 I/O 操作。因为创建直接缓冲区所增加的成本,所以直接缓冲区只用于长生存期的缓冲区,而不用于短生存期、一次性且用完就丢弃的缓冲区。而且,只能在 ByteBuffer 这个级别上创建直接缓冲区,如果希望使用其它类型,则必须将 Buffer 转换成更具体的类型。判断一个缓冲区是否是直接缓冲区,可以调用isDirect()方法。有三种方式来获取一个缓冲区的对象:a. 调用allocate()或者allocateDirect()方法直接分配,其中allocateDirect()返回的是直接缓冲区。b. 包装一个数组,如: byte[] b = new byte[1024]; ByteBuffer bb = ByteBuffer.wrap(b);c. 内存映射,即调用FileChannel的map()方法。缓冲区基本属性这几个属性是每个缓冲区都有的并且是常用的操作。a. 容量(capacity),缓冲区大小b. 限制(limit),第一个不应被读取或写入的字节的索引,总是小于容量。c. 位置(position),下一个被读取或写入的字节的索引,总是小于限制。d. clear()方法:设置limit为capacity,position为0。e. filp()方法:设置limit为当前position,然后设置position为0。f. rewind()方法:保持limit不变,设置position为0。缓冲区数据操作操作包括了读取和写入数据两种。读取数据使用get()及其系列方法,除boolean外,每一种类型包括了对应的get()方法,如getInt(),getChar()等,get()方法用来读取字节,支持相对和绝对索引两种方式。写入数据使用put()及其系列方法,和get()方法是对应的。package nio;import java.io.FileInputStream;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class BufferDemo ...{ public static void main(String[] args) throws Exception...{ //分配一个非直接缓冲区 ByteBuffer bb = ByteBuffer.allocate(100); //向缓冲区写入0到100的字节制 for(int i = 0; i <100; i++)...{ byte b = (byte) (Math.random() * 100); bb.put(b); } System.out.println("写入文件前的缓冲区数据"); bb.flip(); while(bb.hasRemaining()) System.out.print(bb.get() + " "); System.out.println(); //获取一个关联到文件buffer.txt的信道 FileChannel fc = new FileOutputStream("buffer.txt").getChannel(); //将缓冲区数据写到文件中 bb.flip(); fc.write(bb); //防止缓存 fc.force(true); //关闭信道 fc.close(); bb = null; fc = null; //下面从文件中读取数据 fc = new FileInputStream("buffer.txt").getChannel(); ByteBuffer bb2 = ByteBuffer.allocate((int) fc.size()); fc.read(bb2); System.out.println("从文件读取的缓冲区数据"); bb2.flip(); while(bb2.hasRemaining()) System.out.print(bb2.get() + " "); System.out.println(); fc.close(); bb2 = null; fc = null; }}内存映射文件
第 8 种 Buffer 类型 MappedByteBuffer 只是一种特殊的 ByteBuffer 。 MappedByteBuffer 将文件所在区域直接映射到内存。通常,该区域包含整个文件,但也可以只映射部分文件。所以,必须指定要映射文件的哪部分。而且,与其它 Buffer 对象一样,这里没有构造函数;必须让 java.nio.channels.FileChannel 的 map() 方法来获取 MappedByteBuffer 。此外,无需过多涉及通道就可以用 getChannel() 方法从 FileInputStream 或 FileOutputStream 获取 FileChannel 。通过从命令行传入文件名来读取文本文件的内容,清单 4 显示了 MappedByteBuffer :
清单 4. 读取内存映射文本文件import java.io.*;import java.nio.*;import java.nio.channels.*;import java.nio.charset.*;public class ReadFileBuff { public static void main(String args[]) throws IOException { if (args.length != 0) { String filename = args[0]; FileInputStream fis = new FileInputStream(filename); FileChannel channel = fis.getChannel(); int length = (int)channel.size(); MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length); Charset charset = Charset.forName("ISO-8859-1"); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(byteBuffer); for (int i=0, n=charBuffer.length(); i<n; i++) { System.out.print(charBuffer.get()); } } }}
Powered by: BlogJava Copyright © 比特鼠