Java I/O的缓冲器细节

1.Buffer的属性

    每个Buffer都有以下的属性:


   capacity :容量;表示这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。


    limit:界限;在Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时,limit代表buffer中有效数据的长度。

   position:位置;读/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后,buffer会更新下标的值。调用position()可返回当前位置。

   mark:标记;一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。


    这些属性总是满足以下条件:0 <= mark <= position <= limit <= capacity

    Buffer的主要方法如下:

      clear():清空缓冲区,将position设置为0,limit设置为容量。调用该方法为覆写缓冲区做好准备。

      flip():将limit设置为position,position设置为0。该方法位于准备从缓冲区中读取已经写入的数据。

      limit():返回limit的值;

      limit(int lim):  设置limit的值

      position():返回position的值

      position(int pos):设置position的值

      remaining():返回(limit - position)

      hasRemaining():若有介于position和limit之间的元素,则返回true

      rewind():将position设置为0,limit不变,一般在把数据重写入Buffer前调用

      mark():将mark设置为position

      reset():将position设置到mark

2.ByteBuffer

     在Buffer的众多子类中,ByteBuffer是一个地位特殊的类。它是唯一与通道交互的缓冲器,是将数据移进移出通道的唯一方式。ByteBuffer是一个抽象类,不能直接创建类的实例,但该类中定义了4个static方法来做创建对应的Buffer:

    ByteBuffer allocate(int capacity) //创建一个指定capacity的ByteBuffer。
    ByteBuffer allocateDirect(int capacity) //创建一个direct的ByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好
    ByteBuffer wrap(byte [] array) //将制定的byte数组包装成一个bytebuffer
    ByteBuffer wrap(byte [] array, int offset, int length) //把一个byte数组或byte数组的一部分包装成ByteBuffer


    ByteBuffer定义了一系列get和put操作来从中读写byte数据,如下面几个:
       byte get()     //会更新position的位置
       ByteBuffer get(byte [] dst) //向dst中写入数据,会改变position位置
       byte get(int index)     //不会更新position的位置
       ByteBuffer put(byte b)   //会更新position的位置
       ByteBuffer put(byte [] src)  //将src中的数据写入Buffer,会改变position位置
       ByteBuffer put(int index, byte b)   //不会更新position的位置

    这些操作可分为绝对定位和相对定为两种,相对定位的读写操作依靠position来定位Buffer中的位置,并在操作完成后会更新position的值。在其它类型的buffer中,也定义了相同的函数来读写数据,唯一不同的就是一些参数和返回值的类型。除了get(int index)、getXxx(int index)和putXxx(int index, Xxx value)这些指定读取和插入数据的位置的绝对方法外,其他方法均为相对方法。每个put方法的调用结果将返回操作后的Buffer对象,因此可以连续调用put()方法,如:ByteBuffer.putInt(0xCAFEBABE).putShort(3).putShort(45);

     由ByteBuffer通过“as”方法产生的其他类型的Buffer也同样具有如上的这些相对和绝对get和put方法。


    在ByteBuffer中,数据默认下是以大端形式存储,即高位优先(低地址存放高字节),也可以设置为使用小端方式存储数据,使用order(ByteOrder ord)方法修改存储方式,ord的值可取为:ByteOrder.BIG_ENDIAN或者ByteOrder.LITTLE_ENDIAN两者之一。


     视图缓冲器可以让我们通过某个特定的基本数据类型的视窗查看底层的ByteBuffer。ByteBuffer依然是实际存储数据的地方,“支持”在它上建立的其他视图,对这些视图的任何修改都会映射成对ByteBuffer中数据的修改。利用asCharBuffer、asShortBuffer等“asXXXBuffer”方法可以获取ByteBuffer上对应的视图。当我们需要向ByteBuffer中写入数据时,可以使用ByteBuffer自带的put/get方法来操作,也可以建立一个对应的视图,通过视图的put/get方法来操作数据。如下图所示为ByteBuffer和其他视图的转换:

                                                            

           字符集:Charset;编码(Encode):把明文的字符序列转换成计算机能够理解的二进制序列;解码(Decode):把二进制数据转换成明文字符串。JDK提供了Charset类来处理字节序列和字符序列(字符串)之间的转换关系。

      Charset类中的静态方法availableCharsets()返回当前JDK所支持的所有字符集。一旦知道了某个字符集的别名,就可以创建 对应的Charset对象,使用forName()方法传入字符集别名来创建;随后根据需要调用字符集对象的newEncoder()方法或者newDecoder()方法,分别返回与该字符集对应的编码器和解码器,调用编码器的encode方法可以将字符序列编码为字节序列,而调用decode()方法则将字节序列解码为字符序列。如下代码:

Charset gbk = Charset.forName("GBK");  //返回GBK字符集对应的字符集对象
CharsetEncoder gbkEncoder = gbk.newEncoder();//创建该字符集对应的编码器
CharsetEncoder gbkDecoder = gbk.newDecoder();//创建该字符集对应的解码器
ByteBuffer bb = gbkEncoder.encode(CharBuffer in); //使用encode方法将CharBuffer编码为ByteBuffer
CharBuffer cb = gbkDecoder.decode(ByteBuffer in); //解码

3.ByteBuffer与FileChannel交互

      使用FileInputStream、FileOutputStream、RandomAccessFile对应的getChannel()方法可以返回文件对应的FileChannel,使用ByteBuffer即可和FileChannel进行交互,代码如下:

//: io/GetChannel.java
// Getting channels from streams
package tij;
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
public class GetChannel {
private static final int BSIZE = 1024;
public static void main(String[] args) throws Exception {
// Write a file:
FileChannel fc =
new FileOutputStream("data.txt").getChannel();
fc.write(ByteBuffer.wrap("Some text ".getBytes()));
fc.close();
// Add to the end of the file:
fc =
new RandomAccessFile("data.txt", "rw").getChannel();
fc.position(fc.size()); // Move to the end
fc.write(ByteBuffer.wrap("Some more".getBytes()));
fc.close();
// Read the file:
fc = new FileInputStream("data.txt").getChannel();
ByteBuffer buff = ByteBuffer.allocate(BSIZE);
fc.read(buff);
buff.flip();
while(buff.hasRemaining())
System.out.print((char)buff.get());
}
} /* Output:
Some text Some more
*///:~

4.内存映射文件

     内存映射文件允许将文件的部分或者全部直接映射成Buffer,程序通过Buffer操作文件。
     MappedByteBuffer是ByteBuffer的子类,通过调用FileChannel对应的map()方法返回这个Buffer对象,用于将文件的内容映射成Buffer。map()方法的签名如下:

              MappedByteBuffer map(FileChannel.MapMode mode, long position, long  size);

     第一个参数为执行映射时的模式,分别有只读(FileChannel.MapMode.READ_ONLY),读写(FileChannel.MapMode.READ_WRITE)等模式。而第二个、第三个参数用于控制将
Channel哪些数据映射成ByteBuffer。映射完成之后,通过write和read的一系列重装方法,对Buffer进行读写。

    文件加锁:FileChannel调用tryLock()和Lock()方法可以对文件进行加锁,返回FileLock对象。tryLock()方法是非阻塞的,设法获取锁,如果不能获得,则返回;而lock()方法是阻塞的,直到获得锁。获得锁以后可以调用release()方法释放对应锁。也可以只对文件的一部分进行加锁。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值