Java NIO - Buffer

173 阅读2分钟

我们知道JAVA NIO包含三大核心概念:缓存(buffer),通道(channel)和选择器(selector)。今天我们先看一下Buffer

NIO中缓存的作用跟传统IO一样,都是为了提高读写性能而创建。但是在具体的使用细节上有所不同,为了方便对比,我们先看一下传统IObuffer的使用。

传统IO中buffer的使用

在传统的IO中,不管是输出流还是输入流,我们都可以使用buffer来提高读写性能,具体来说,buffer的创建可以分为两类

自己创建buffer

我们可以根据情况创建自己的buffer,比如我们要读取内容:Hello world!,然后输出:

try(InputStream in = new ByteArrayInputStream("Hello world!".getBytes())){
  byte[] buffer = new byte[5];
  int data;
  while ((data = in.read(buffer)) != -1){
    for(int i=0; i < data; i++){
      System.out.print((char)buffer[i]);
    }
  }
}
catch (IOException e){
  e.printStackTrace();
}

上例中我们显示地创建了一个长度为5的buffer(字节数组),你还可以根据情况创建字符数组等,然后在把内容写到buffer中,再读取buffer

使用IO库中自带的buffer

除了自己创建,你也可以使用IO库中的BufferedInputStreamBufferedOutputStream自带的buffer

try(InputStream in = new ByteArrayInputStream("Hello world!".getBytes())){
  BufferedInputStream bufferedIn = new BufferedInputStream(in,5);
  int data;
  while ((data = bufferedIn.read())!= -1){
    System.out.print((char)data);
  }
}
catch (IOException e){
  e.printStackTrace();
}

上例使用了BufferedInputStream类,并初始化了一个长度为内部buffer

注意:上例中的read读取的就是in内部buffer,可以看一下BufferedInputStreamread()源码:

public synchronized int read() throws IOException {
    if (pos >= count) {
        fill();
        if (pos >= count)
            return -1;
    }
    return getBufIfOpen()[pos++] & 0xff;
}

NIO中的Buffer

NIO中的buffer也分为多个类型:ByteBufferCharBuffeDoubleBuffer等,我们以ByteBuffer为例来看一下如何在NIO中使用buffer

try{
  RandomAccessFile rfa = new RandomAccessFile("d:\\temp1.txt","rw");
  FileChannel channel = rfa.getChannel();
  ByteBuffer buffer = ByteBuffer.allocate(20);
  while (channel.read(buffer) != -1){
    buffer.flip();
    while (buffer.hasRemaining()){
      System.out.print((char) buffer.get());
    }
    buffer.clear();
  }
  channel.close();
}
catch (IOException e){
  e.printStackTrace();
}

上面实例化了一个长度为20的buffer,不过在NIO中有了专门的缓存对象来处理不同的数据类型。此外,还应注意以下区别: 1.在读取buffer中的内容时,必须要先调用flip()方法,确保channel从写模式切换到了读模式 2.使用get()方法读取内容 3.当读取完毕后使用clear()来关闭buffer。一定要记得调用clear(),否则将导致上例中外层while死循环

注意:clear方法并没有真正清除数据,只是设置了position为0。因为方法内部能保证position,limit无论在读和写时都能指向正确位置,所以即使不清除已读数据,也不会导致数据混乱。