java NIO入门篇——缓冲区Buffer

什么是缓冲区?

java 传统I/O将输入输出抽象为字节流或字符流,是基于单个字节的,优点是使用简单,缺点就是效率低下。而且IO的各种流是阻塞的。当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入,在此期间不能再干任何事情了。而Java NIO则提供了缓冲区Buffer来实现字节块的读写,放在java.nio包下面。常用的Buffer如下:

在这里插入图片描述

通过继承图我们可以知道所有的缓冲区实现类都继承自Buffer,Buffer具有一下几个主要属性:

  	private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;
    Buffer(int mark, int pos, int lim, int cap) {       // package-private
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }
     public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }
主要属性 含义
mark 位置标记,可以临时保存position,用于position改变后可以回复原来位置
position 表示缓冲区中当前可读写位置,也就是索引
limit 表示缓冲区中可用元素的大小,也就是实际存放的字节数量
capacity 表示缓冲的大小,其实就是缓冲区内部数组的大小

查看源码,我们可以知道每个具体的Buffer子类内部管理着一个字节数组,数据存放在数组中。

ByteBuffer(int mark, int pos, int lim, int cap,   // package-private
                 byte[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }
  
IntBuffer(int mark, int pos, int lim, int cap,   // package-private
                 int[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }
FloatBuffer(int mark, int pos, int lim, int cap,   // package-private
                 float[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

Buffer作为数据的载体,Java程序在使用NIO时都会通过Buffer与外界进行通信。

缓冲区的使用

Buffer提供了一下主要方法,如图:
在这里插入图片描述

1.allocate、put、flip、get、rewind、clear

为了自己学习方便,自己参考视频与文章,亲手画了一下图解,便于大家理解
在这里插入图片描述
在这里插入图片描述
以下是具体的使用测试示例:

@Test
    public void test01(){
        String str = "javaNIO";
        //1.分配一个指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(9);
        showIConsole(buffer,"allocate()");
        //2.put()存入数据到缓冲区
        buffer.put(str.getBytes());
        showIConsole(buffer,"put()");
        //3.切换到读取模式
        buffer.flip();
        showIConsole(buffer,"flip()");
        //4.get()读取缓冲区中的数据
        byte[] bytes = new byte[buffer.limit()];
        buffer.get(bytes);
        System.out.println("读到的数据:"+new String(bytes,0,bytes.length));
        showIConsole(buffer,"get()");
        //5.rewind():倒回,可重复读
        buffer.rewind();
        showIConsole(buffer,"rewind");
        //6.clear(),清空缓冲区,但数据依然存在,只是处于"被遗忘状态”
        buffer.clear();
        showIConsole(buffer,"clear()");
        System.out.println("after clear,the part data:"+ (char)buffer.get(5));
    }
     public void showIConsole(Buffer buf, String title){
        System.out.println("-------"+title+"-------");
        System.out.println("position:"+buf.position());
        System.out.println("limt:"+buf.limit());
        System.out.println("capacity:"+buf.capacity());

    }

运行结果:

/home/xiaoxin/jdk/jdk1.8/bin/java...
-------allocate()-------
position:0
limt:9
capacity:9
-------put()-------
position:7
limt:9
capacity:9
-------flip()-------
position:0
limt:7
capacity:9
读到的数据:javaNIO
-------get()-------
position:7
limt:7
capacity:9
-------rewind-------
position:0
limt:7
capacity:9
-------clear()-------
position:0
limt:9
capacity:9
after clear,the part data:I

Process finished with exit code 0

2.mark测试

在这里插入图片描述

扫描二维码关注公众号,回复: 9150259 查看本文章
 @Test
    public  void test02(){
        ByteBuffer buffer = ByteBuffer.allocate(9);
        buffer.put("javaNIO".getBytes());
        buffer.flip();
        byte[] dst = new byte[buffer.limit()];
        buffer.get(dst,0,2);
        System.out.println(new String(dst,0,4));
        System.out.println("before mark,the position:"+buffer.position());
        System.out.println("before mark,the position:"+buffer.limit());
        System.out.println("before mark,the position:"+buffer.capacity());
        //标记
        buffer.mark();
        System.out.println("------after mark,read again---");
        buffer.get(dst,2,2);
        System.out.println(new String(dst,2,2));
        System.out.println("after mark,the position:"+buffer.position());
        System.out.println("after mark,the position:"+buffer.limit());
        System.out.println("after mark,the position:"+buffer.capacity());
        //reset,恢复到mark位置
        buffer.reset();
        System.out.println("after reset,the position:"+buffer.position());
        System.out.println("after reset,the position:"+buffer.limit());
        System.out.println("after reset,the position:"+buffer.capacity());
        //判断缓冲区是否还有数据
        if(buffer.hasRemaining()){
            //获取缓冲区中可以操作的数量
            System.out.println(buffer.remaining());
        }
    }

运行结果:

/home/xiaoxin/jdk/jdk1.8/bin/java 
ja  
before mark,the position:2
before mark,the position:7
before mark,the position:9
------after mark,read again---
va
after mark,the position:4
after mark,the position:7
after mark,the position:9
after reset,the position:2
after reset,the position:7
after reset,the position:9
5

3.直接缓冲区

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

@Test
    public void test03(){
        //分配直接缓冲区
        ByteBuffer buffer= ByteBuffer.allocateDirect(512);
        showIConsole(buffer,"allocate direct Bytebuffer");
        System.out.println(buffer.isDirect());
        System.out.println(buffer.isReadOnly());
    }

运行结果:

/home/xiaoxin/jdk/jdk1.8/bin/java
-------allocate direct Bytebuffer-------
position:0
limt:512
capacity:512
true
false

Process finished with exit code 0

4.compact测试

如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先写些数据,那么使用compact()方法。compact()方法将所有未读的数据拷贝到Buffer起始处。compacth调用后buffer 会回到读剩余后的状态,将position设到最后一个未读元素正后面,不会覆盖未读的数据。另外与clear不同的是compact后的数据不会被遗忘,可以继续读。比如intbuffer连续放入两个int数字,get读出一个后,再调用compact后,position、limit,compacity分别为1、9、9,切后续继续get数据,获得99。但若此时不是使用compact而是使用clear,则是0,9,9且后续get,则是position为0的数据199了。

 @Test
    public void test04(){
        IntBuffer intBuffer = IntBuffer.allocate(9);
        showIConsole(intBuffer,"intbuffer-allocate");
        intBuffer.put(199);
        intBuffer.put(99);
        showIConsole(intBuffer,"intbuffer-put");
        intBuffer.flip();
        showIConsole(intBuffer,"intbuffer-flip");
        int n= intBuffer.get();
        System.out.println("get1:"+n);
        showIConsole(intBuffer,"intbuffer-get");
        intBuffer.compact();
        showIConsole(intBuffer,"intbuffer-compct");
        showIConsole(intBuffer,"intbuffer-get2");
        System.out.println("get2:"+intBuffer.get());

    }

结果:

/home/xiaoxin/jdk/jdk1.8/bin/java 
-------intbuffer-allocate-------
position:0
limt:9
capacity:9
-------intbuffer-put-------
position:2
limt:9
capacity:9
-------intbuffer-flip-------
position:0
limt:2
capacity:9
get1:199
-------intbuffer-get-------
position:1
limt:2
capacity:9
-------intbuffer-compct-------
position:1
limt:9
capacity:9
-------intbuffer-get2-------
position:1
limt:9
capacity:9
get2:99

Process finished with exit code 0

对比clear,进一步体会不同之处:

@Test
    public void test05(){
        IntBuffer intBuffer = IntBuffer.allocate(9);
        showIConsole(intBuffer,"intbuffer-allocate");
        intBuffer.put(199);
        intBuffer.put(99);
        showIConsole(intBuffer,"intbuffer-put");
        intBuffer.flip();
        showIConsole(intBuffer,"intbuffer-flip");
        int n= intBuffer.get();
        System.out.println("get1:"+n);
        showIConsole(intBuffer,"intbuffer-get");
        intBuffer.clear();//此时使用clear而不是compact
        showIConsole(intBuffer,"intbuffer-clear instead of compct");
        showIConsole(intBuffer,"intbuffer-get2");
        //position回到了0.ge得到199,而compact则是读取第一个剩余后的99,position为1
        System.out.println("get2:"+intBuffer.get());

    }

clear结果:

/home/xiaoxin/jdk/jdk1.8/bin/java
-------intbuffer-allocate-------
position:0
limt:9
capacity:9
-------intbuffer-put-------
position:2
limt:9
capacity:9
-------intbuffer-flip-------
position:0
limt:2
capacity:9
get1:199
-------intbuffer-get-------
position:1
limt:2
capacity:9
-------intbuffer-clear instead of compct-------
position:0
limt:9
capacity:9
-------intbuffer-get2-------
position:0
limt:9
capacity:9
get2:199

Process finished with exit code 0

总结

传统 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据, I/O 通常相当慢。一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。NIO性能的优势就来源于缓冲的机制,不管是读或者写都需要以块的形式写入到缓冲区中。NIO实际上让我们对IO的操作更接近于操作系统的实际过程。

发布了17 篇原创文章 · 获赞 2 · 访问量 675

猜你喜欢

转载自blog.csdn.net/qq_43615903/article/details/104241785
今日推荐