netty学习笔记java io回顾二之Buffer

342 阅读2分钟

java.io中最为核心的一个概念是流(Stream),面向流编程。Java中,一个流要么是输入流,要么是输出流,不可能同时既是输入流又是输出流。
java.nio中拥有三个核心概念:Selector、Channel与Buffer。在java.nio中,我们是面向块(block)或是缓冲区(buffer)编程的。Buffer本身就是一块内存,底层实现上实际是个数组。数据的读写都是通过Buffer来实现的。
除了数组之外,Buffer还提供了对于数据的结构化访问,并且可以追踪到系统的读写过程。

Java中的7种原生数据类型都有各种对应的Buffer类型,如IntBuffer、LongBuffer、ByteBuffer及CharBuffer等,并没有BooleanBuffer类型。
Channel指的是可以向其写入数据或者从中读取数据的对象,类似于java.io中的Stream。

所有的数据都是通过读写Buffer来进行的,永远不会出现直接向Channel中写入数据的情况,或者直接从Channel读取数据的情况。

与Stream不同,Channel是双向的,一个流只可能是InputStream或者是OutputStream,Channel打开后则可以进行读取、写入或是读写。

由于Channel是双向的,因此它能更好的反应出底层操作系统的真实情况,在Linux系统中,底层操作系统的通道就是双向的。

关于NIO Buffer中的3个重要状态属性的含义:position、limit、capacity。
先上段小示例代码:

public class NioTest1 {

    public static void main(String[] args) {
        IntBuffer buffer = IntBuffer.allocate(10);

        for (int i = 0; i < 10; i++) {
            int random = new SecureRandom().nextInt(20);
            buffer.put(random);//put向buffer写数据
        }
        //读写转换
        buffer.flip();

        while (buffer.hasRemaining()) {
            System.out.println(buffer.get());//get从buffer读数据
        }
    }
}

Ron Hitchens写的java nio一书中把nio相关的概念介绍的都明明白白的,这里摘抄一下对Buffer的简介:

image.png

  • 容量(Capacity) 缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能 被改变。
  • 上界(Limit) 缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
  • 位置(Position) 下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
  • 标记(Mark) 一个备忘位置。调用 mark( )来设定 mark = postion。调用 reset( )设定 position = mark。标记在设定前是未定义的(undefined)。
    这四个属性之间总是遵循以下关系: 0 <= mark <= position <= limit <= capacity
    调用api的过程中,position、limit、capacity的变化:

image.png

image.png

image.png 最后的小例子:

public class NioTest4 {

    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream("input.txt");
        FileOutputStream outputStream = new FileOutputStream("output.txt");

        FileChannel inputChannel = inputStream.getChannel();
        FileChannel outputChannel = outputStream.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(512);
        while (true) {
            buffer.clear();//如果注释掉该行代码会发生什么情况?
            //注释掉buffer.clear()后, 在while循环的最后一句outputChannel.write执行后
            // buffer的position等于limit,这样read返回为0
            // while循环无法结束,flip会导致每次把buffer的内容重复写入
            int read = inputChannel.read(buffer);
            System.out.println(read);
            if (read == -1) {
                break;
            }
            buffer.flip();
            outputChannel.write(buffer);
        }
        inputChannel.close();
        outputChannel.close();
    }
}