前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NIO学习一

NIO学习一

作者头像
路行的亚洲
发布2020-07-16 15:59:13
7110
发布2020-07-16 15:59:13
举报
文章被收录于专栏:后端技术学习
代码语言:javascript
复制
NIO相比普通IO提供了功能更为强大、处理数据更快的解决方案。
常用于高性能服务器上。NIO实现高性能处理的原理是使用较少的线程来处理更多的任务
常规io使用的byte[]、char[]进行封装,而NIO采用ByteBuffer类来操作数据,再结合
针对File或socket技术的channel,采用同步非阻塞技术来实现高性能处理,而Netty
正是采用ByteBuffer(缓冲区)、Channel(通道)、Selector(选择器)进行封装的。
因此我们需要先了解NIO相关的知识。

NIO相关知识:首先来了解ByteBuffer和CharBuffer,此时需要了解父类Buffer.

因此需要了解ByteBuffer的底层是否是基于数组实现的?可以看到如果是直接allocate,则不是,而是将元素的信息存入内存中,此时不经过数组,而allocate,则会存入到数组中。同时对于flip翻转有什么作用?我们是否可以自己实现翻转行为?答案是肯定的,同时将上界调为当前位置的大小,通过改变当前位置为0,即可实现,因为翻转的本质是为了实现对数据的获取,将数据进行取出。了解标记mark是什么,了解clear什么,了解hasRemaining()是什么,什么是rewind?什么是偏移量ArrayOffset?缓冲区的标记是一个索引,在调用reset()方法时,会将缓冲区的postion位置重置为该索引。同时mark不是必须的。如果将postion或limit调整为小于该mark值时,该mark会被丢弃,丢弃后mark的值为-1。如果未定义mark,调用reset()方法时会抛出InvalidMarkException异常。clear的作用是还原缓冲区到初始的状态,包含将位置设置为0,将限制设置为容量,并丢弃标记,即一切为默认。从源码中,我们看到,当前位置为0,上界等于容量,同时mark=-1。hasRemaing是判断当前位置到限制之间是否有元素,而remaing则是计算它们之间还有多少个元素。可以看到一开始时,字节数组是没有偏移量的,当时如果将postion设置为大于1的数时,也即当前位置大于0时,此时就会出现偏移量。因为偏移量是相对的。

首先其来自于java.nio下的Buffer:

代码语言:javascript
复制
public abstract class Buffer {
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

    // 值的关系:mark <= position <= limit <= capacity
    //也即标记<=位置<=限制<=容量,如果位置、限制<标记,则该标记会被丢弃,
    //如果未定义mark,则reset时会被抛弃
    //标记
    private int mark = -1;
    //位置  不能为负数
    private int position = 0
    //限制 不能为负数   
    private int limit;
    //容量 不能为负数
    private int capacity;

    //地址,偏移量
    long address;

    //构造方法:在检查完不变量后创建一个新的带标记、位置、限制、容量的buffer
    Buffer(int mark, int pos, int lim, int cap) {
        //检查cap、mark是否<0,
        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 int capacity() {
        return capacity;
    }

    //当前位置
    public final int position() {
        return position;
    }

    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        //对mark进行判断,从而是不是-1
        if (mark > position) mark = -1;
        return this;
    }

    public final int limit() {
        return limit;
    }

    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 mark() {
        mark = position;
        return this;
    }

    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

    //清除,此时当前位置为0,同时清除标记,同时将上界变成了容量
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

    //翻转
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    //重绕此缓冲区,将位置设置为0并丢弃标记
    //可以看到将当前位置变成了0
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

    //返回当前元素与limit之间的元素的数量
    public final int remaining() {
        return limit - position;
    }

    //当前位置是否小于上界,也即还是否有元素
    public final boolean hasRemaining() {
        return position < limit;
    }

    public abstract boolean isReadOnly();

    //是否是数组
    public abstract boolean hasArray();

    public abstract Object array();

    //数组偏移量
    public abstract int arrayOffset();

    public abstract boolean isDirect();

    final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }

    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }

    final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }

    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }


    final int checkIndex(int i) {                       // package-private
        if ((i < 0) || (i >= limit))
            throw new IndexOutOfBoundsException();
        return i;
    }

    //检查索引
    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }

    final int markValue() {                             // package-private
        return mark;
    }

    final void truncate() {                             // package-private
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }

    final void discardMark() {                          // package-private
        mark = -1;
    }

    //检查边界:偏移量、长度、大小
    static void checkBounds(int off, int len, int size) { // package-private
        if ((off | len | (off + len) | (size - (off + len))) < 0)
            throw new IndexOutOfBoundsException();
    }

}

从包中,我们可以看到Buffer的子类抽象类包含的信息7个子类,常用的CharBuffer、ByteBuffer:

代码语言:javascript
复制
public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>
public abstract class CharBuffer extends Buffer implements Comparable<CharBuffer>, Appendable, CharSequence, Readable
public abstract class DoubleBuffer extends Buffer implements Comparable<DoubleBuffer>   
public abstract class FloatBuffer extends Buffer implements Comparable<FloatBuffer>  
public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer>  
public abstract class LongBuffer extends Buffer implements Comparable<LongBuffer>
public abstract class ShortBuffer extends Buffer implements Comparable<ShortBuffer>

但是我们可以看到其7个子类也是抽象的,不能直接new实例化。那我们如何创建这些类的对象呢?使用的方式将上面7种数据类型的数组包装进缓冲区中,此时需要借助静态方法wrap()进行实现。wrap()方法的作用是将数组放入缓冲区中,来构建存储不同数据类型的缓冲区。比如byteBuffer

代码语言:javascript
复制
 public static ByteBuffer wrap(byte[] array) {
        //将byte包装成byteBuffer,包装的过程中传入的偏移量为0,同时长度为字节数组的长度
        return wrap(array, 0, array.length);
 }

public static ByteBuffer wrap(byte[] array, int offset, int length){
      try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }

//堆字节数组:本质是一个字节数组byte[]
 HeapByteBuffer(byte[] buf, int off, int len) { 
        //从这里我们可以看到标记值为-1
        super(-1, off, off + len, buf.length, buf, 0);
}

 ByteBuffer(int mark, int pos, int lim, int cap,byte[] hb, int offset){
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
}

 Buffer(int mark, int pos, int lim, int cap) {
        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;
        }
    }

下面我们来了解相关特性:

代码语言:javascript
复制
/**
 * 进行nio测试:可以看到ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
 * 是抽象类,而wrap()就相当于创建这些缓冲区的工厂方法,都会经过wrap(array, 0, array.length)方法,比如ByteBuffer会经过
 * //可以看到hb是一个字节数组
 * ByteBuffer(int mark, int pos, int lim, int cap,byte[] hb, int offset) {
 * //调用buffer的构造方法
 * super(mark, pos, lim, cap);
 * //仅堆缓冲区为非null
 * this.hb = hb;
 * //偏移量
 * this.offset = offset;
 * }
 * <p>
 * ByteBuffer类缓冲区的技术原理就是使用byte[]数组进行数据保存
 * 缓冲区存储的数据还是存储在byte[]字节数组中。使用缓冲区与使用byte[]字节数组相比:
 * 优点在于缓冲区将存储数据的byte[]字节数组内容与相关的信息整合在1个Buffer类中,将
 * 数据与缓冲区中的信息进行了整合,并进行了封装,这样便于得到相关的信息和处理数据
 */
public class NIOTest {
    public static void main(String[] args) {
        byte[] byteArray = new byte[]{1, 2, 3};
        short[] shortArray = new short[]{1, 2, 3, 4};

        int[] intArray = new int[]{1, 2, 3, 4, 5};
        long[] longArray = new long[]{1, 2, 3, 4, 5, 6, 7};
        float[] floatArray = new float[]{1, 2, 3, 4, 5, 6, 7};
        double[] doubleArray = new double[]{1, 2, 3, 4, 5, 6, 7, 8};
        char[] charArray = new char[]{'a', 'b', 'c', 'd'};

        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        ShortBuffer shortBuffer = ShortBuffer.wrap(shortArray);
        IntBuffer intBuffer = IntBuffer.wrap(intArray);
        LongBuffer longBuffer = LongBuffer.wrap(longArray);
        FloatBuffer floatBuffer = FloatBuffer.wrap(floatArray);
        DoubleBuffer doubleBuffer = DoubleBuffer.wrap(doubleArray);
        CharBuffer charBuffer = CharBuffer.wrap(charArray);

        /**
         * 返回结果:
         * byteBuffer=java.nio.HeapByteBuffer
         * shortBuffer=java.nio.HeapShortBuffer
         * intBuffer=java.nio.HeapIntBuffer
         * longBuffer=java.nio.HeapLongBuffer
         * floatBuffer=java.nio.HeapFloatBuffer
         * doubleBuffer=java.nio.HeapDoubleBuffer
         * charBuffer=java.nio.HeapCharBuffer
         */
        System.out.println("byteBuffer=" + byteBuffer.getClass().getName());
        System.out.println("shortBuffer=" + shortBuffer.getClass().getName());
        System.out.println("intBuffer=" + intBuffer.getClass().getName());
        System.out.println("longBuffer=" + longBuffer.getClass().getName());
        System.out.println("floatBuffer=" + floatBuffer.getClass().getName());
        System.out.println("doubleBuffer=" + doubleBuffer.getClass().getName());
        System.out.println("charBuffer=" + charBuffer.getClass().getName());

        System.out.println("======================================");

        System.out.println("byteBufffer.capacity=" + byteBuffer.capacity());
        System.out.println("shortBuffer.capacity=" + shortBuffer.capacity());
        System.out.println("intBuffer.capacity=" + intBuffer.capacity());
        System.out.println("longBuffer.capacity=" + longBuffer.capacity());
        System.out.println("floatBuffer.capacity=" + floatBuffer.capacity());
        System.out.println("doubleBuffer.capacity=" + doubleBuffer.capacity());
        System.out.println("charBuffer.capacity=" + charBuffer.capacity());

    }
}

运行结果:

代码语言:javascript
复制
byteBuffer=java.nio.HeapByteBuffer
shortBuffer=java.nio.HeapShortBuffer
intBuffer=java.nio.HeapIntBuffer
longBuffer=java.nio.HeapLongBuffer
floatBuffer=java.nio.HeapFloatBuffer
doubleBuffer=java.nio.HeapDoubleBuffer
charBuffer=java.nio.HeapCharBuffer
======================================
byteBufffer.capacity=3
shortBuffer.capacity=4
intBuffer.capacity=5
longBuffer.capacity=7
floatBuffer.capacity=7
doubleBuffer.capacity=8
charBuffer.capacity=4

可以看到它会调用包装方法,进行包装也即上面所看到的wrap方法。最终是一个和堆有关的buffer的字节数组。

代码语言:javascript
复制
/**
 * 进行limit和position使用
 */
public class NIOTest2 {
    public static void main(String[] args) {
        char[] charArray = new char[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
        CharBuffer buffer = CharBuffer.wrap(charArray);
        System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit());
        buffer.limit(4);
        //buffer.position(2);
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit());
        buffer.put(0, '0');
        buffer.put(1, 'h');
        buffer.put(2, '3');
        buffer.put(3, 'i');
        //会在这里抛异常,因为指定了现在4个数据
        buffer.put(4, 'j');
        buffer.put(5, 'k');
    }
}

运行结果:

代码语言:javascript
复制
Exception in thread "main" java.lang.IndexOutOfBoundsException
    at java.nio.Buffer.checkIndex(Buffer.java:540)
    at java.nio.HeapCharBuffer.put(HeapCharBuffer.java:178)
    at com.study.nio.NIOTest2.main(NIOTest2.java:21)
A capacity()=8 limit()=8
B capacity()=8 limit()=4

从运行的结果中,我们可以看到原来包装的buffer的容量是8,同时上界是8。而设置上界为4,此时当添加数据到第四个位置时,会抛异常。

代码语言:javascript
复制
/**
 * 进行limit和position、remaining的使用
 */
public class NIOTest3 {
    public static void main(String[] args) {
        char[] charArray = new char[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
        CharBuffer buffer = CharBuffer.wrap(charArray);
        System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        buffer.limit(5);
        //buffer的下一个位置是2号角标的位置
        buffer.position(2);

        //remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
        System.out.println("remaining()=" + buffer.remaining());
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        buffer.put('i'); //第三个数据c会变成i
        for (int i = 0; i < charArray.length; i++) {
            System.out.println(charArray[i] + " ");
        }
    }
}

运行结果:

代码语言:javascript
复制
A capacity()=8 limit()=8 position()=0
remaining()=3
B capacity()=8 limit()=5 position()=2
a b i d d e f g 
代码语言:javascript
复制
/**
 * 使用buffer mark()方法处理标记
 * mark()的作用:在此缓冲区的位置设置标记,
 * 缓冲去的标记是一个索引,在调用reset()方法时,
 * 会将缓冲区的position位置重置为索引位置。
 * <p>
 * 如果定义了mark,则在将postion或limit调整为小于该mark的值时,该mark会被丢弃,丢弃后mark的值为-1
 * 如果未定义mark,调用reset会抛出InvalidMarkException异常
 * 同时可以看到默认是不开启直接缓冲区的,需要手动设置,此时在jvm和硬盘之间可以少了一个中间缓冲区,提高程序运行的效率
 */
public class NIOTest4 {
    public static void main(String[] args) {
        byte[] byteArray = new byte[]{'a', 'b', 'c', 'd', 'd', 'e', 'f'};
        ByteBuffer buffer = ByteBuffer.wrap(byteArray);
        System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        buffer.limit(5);
        //buffer的下一个位置是2号角标的位置
        buffer.position(2);
        //在位置2设置mark
        buffer.mark();
        System.out.println("remaining()=" + buffer.remaining());
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        buffer.position(3);
        //位置重置,postion的位置会回到mark的位置
        buffer.reset();
        //remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
        System.out.println("remaining()=" + buffer.remaining());
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());

        //可以看到不是直接缓冲区
        System.out.println(buffer.isDirect());

        ByteBuffer byBuffer = ByteBuffer.allocate(100);
        System.out.println("默认关闭,此时调用的不是直接缓冲区:" + byBuffer.isDirect());
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
        System.out.println("此时调用的是直接缓冲区:" + byteBuffer.isDirect());
    }


}

运行结果:

代码语言:javascript
复制
A capacity()=7 limit()=7 position()=0
remaining()=3
B capacity()=7 limit()=5 position()=2
remaining()=3
B capacity()=7 limit()=5 position()=2
false
默认关闭,此时调用的不是直接缓冲区:false
此时调用的是直接缓冲区:true

可以看到如果当前位置为2,上界为5时,如果设置为mark,则此时剩下的元素是3。同时可以看到如果是直接缓冲区,则调用的是内存,否则不是直接缓冲区。

代码语言:javascript
复制
/**
 * 对缓存区进行反转 flip()方法:原理是首先将闲置设置为当前位置,然后将
 * 位置设置为0.如果定义了标记,则丢弃该标记。
 * public Buffer flip(){
 * limit = postion;
 * postion = 0;
 * mark = -1;
 * return this;
 * }
 */
public class NIOTest5 {
    public static void main(String[] args) {
        byte[] byteArray = new byte[]{'a', 'b', 'c', 'd', 'd', 'e'};
        ByteBuffer buffer = ByteBuffer.wrap(byteArray);
        System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        //buffer的下一个位置是2号角标的位置
        buffer.position(2);
        //在位置2设置mark
        buffer.mark();
        buffer.flip();
        System.out.println("remaining()=" + buffer.remaining());
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
        //位置重置,postion的位置会回到mark的位置
        try {
            buffer.reset();
        } catch (Exception e) {
            System.out.println("buffer的mark丢失了。。。");
        }

        //remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
        System.out.println("remaining()=" + buffer.remaining());
        System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());


    }


}

运行结果:

代码语言:javascript
复制
A capacity()=6 limit()=6 position()=0
remaining()=2
B capacity()=6 limit()=2 position()=0
buffer的mark丢失了。。。
remaining()=2
B capacity()=6 limit()=2 position()=0

flip操作会将原来的位置翻转到初始位置。从源码中可以看到其会将postion设置为0。

代码语言:javascript
复制
/**
 * 测试postion和limit的使用,模仿flip行为
 */
public class NIOTest6 {
    public static void main(String[] args) {
        //创建一个CharBuffer,容量为20
        CharBuffer charBuffer = CharBuffer.allocate(20);
        //查看当前位置和限制大小,你可以理解为上界
        System.out.println("A postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //写入字符
        charBuffer.put("一个前行在路上的行路人");
        System.out.println("B postion =" + charBuffer.position() + "limit=" + charBuffer.limit());

        //还原位置到0
        charBuffer.position(0);
        System.out.println("C postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //写入字符
        charBuffer.put("学无止境,你在前行");
        System.out.println("D postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //还原缓冲区的状态
        charBuffer.clear();
        System.out.println("E postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
        //继续写入
        charBuffer.put("我不前行的时候,你也在前行");
        charBuffer.append("我要更努力");

        charBuffer.limit(charBuffer.position());
        charBuffer.position(0);
        for (int i = 0; i < charBuffer.limit(); i++) {
            System.out.print(charBuffer.get());
        }
        System.out.println(" ");
    }


}

运行结果:

代码语言:javascript
复制
A postion = 0 limit=20
B postion =11 limit=20
C postion = 0 limit=20
D postion = 9 limit=20
E postion = 0 limit=20
我不前行的时候,你也在前行我要更努力 

从上面的运行结果看,我们成功模仿了flip的翻转行为。

代码语言:javascript
复制
/**
 * 使用flip解决NIOTest6的行为
 */
public class NIOTest7 {
    public static void main(String[] args) {
        //创建一个CharBuffer,容量为20
        CharBuffer charBuffer = CharBuffer.allocate(20);
        //查看当前位置和限制大小,你可以理解为上界
        System.out.println("A postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //写入字符
        charBuffer.put("一个前行在路上的行路人");
        System.out.println("B postion =" + charBuffer.position() + "limit=" + charBuffer.limit());

        //还原位置到0
        charBuffer.position(0);
        System.out.println("C postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //写入字符
        charBuffer.put("学无止境,你在前行");
        System.out.println("D postion = " + charBuffer.position() + "limit=" + charBuffer.limit());

        //还原缓冲区的状态
        charBuffer.clear();
        System.out.println("E postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
        //继续写入
        charBuffer.put("我不前行的时候,你也在前行");
        charBuffer.append("我要更努力");

        //charBuffer.limit(charBuffer.position());
        //charBuffer.position(0);
        charBuffer.flip();
        for (int i = 0; i < charBuffer.limit(); i++) {
            System.out.print(charBuffer.get());
        }
        System.out.println(" ");
    }
}

也即:

代码语言:javascript
复制
          charBuffer.limit(charBuffer.position());
          charBuffer.position(0);
      =>  charBuffer.flip();
代码语言:javascript
复制
/**
 * 查看缓冲区底层实现是否是数组实现的
 */
public class NIOTest8 {
    public static void main(String[] args) {
        allocateDemo();
        allocateDirectDemo();
    }

    private static void allocateDemo() {
        ByteBuffer byteBuffer = ByteBuffer.allocate(100);
        byteBuffer.put((byte) 1);
        byteBuffer.put((byte) 2);
        //返回true,说明底层是数组实现的,说明数据存储在数组中
        System.out.println("缓冲区的底层基于数组实现:" + byteBuffer.hasArray());
    }

    private static void allocateDirectDemo() {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
        byteBuffer.put((byte) 1);
        byteBuffer.put((byte) 2);
        //返回true,说明底层是数组实现的,而返回false,说明数据并没有直接存储在数组中,而是直接存储在内存中
        System.out.println("直接缓冲区的底层基于数组实现:" + byteBuffer.hasArray());
    }
}

运行结果:

代码语言:javascript
复制
缓冲区的底层基于数组实现:true
直接缓冲区的底层基于数组实现:false
代码语言:javascript
复制
/**
 * 判断当前位置和限制之间是否有剩余元素
 * 进行迭代
 */
public class NIOTest9 {
    public static void main(String[] args) {
        hasRemain();
        hasElementGet();
    }

    //测试还剩下的元素
    private static void hasRemain() {
        byte[] byteArray = new byte[]{1, 2, 3, 4};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        byteBuffer.limit(4);

        byteBuffer.position(2);
        System.out.println("byteBuffer.hasRemaining=" + byteBuffer.hasRemaining() + " " + "byteBuffer.remaining=" + byteBuffer.remaining());
    }

    //使用剩下的特性,进行迭代
    private static void hasElementGet() {
        byte[] byteArray = new byte[]{1, 2, 3, 4, 5};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        int remaining = byteBuffer.remaining();
        for (int i = 0; i < remaining; i++) {
            System.out.print(byteBuffer.get() + "");
        }
        System.out.println("");
        byteBuffer.clear();
        while (byteBuffer.hasRemaining()) {
            System.out.print(byteBuffer.get() + "");
        }

        System.out.println("");
        byteBuffer.clear();
        for (; byteBuffer.hasRemaining() == true; ) {
            System.out.print(byteBuffer.get() + "");
        }
        System.out.println("");
    }

}

运行结果:

代码语言:javascript
复制
byteBuffer.hasRemaining=true byteBuffer.remaining=2
12345
12345
12345
代码语言:javascript
复制
/**
 * 对于flip、clear、rewind之间的区别:
 * remind:标记清除,位置postion值归0,limit不变
 * public final Buffer rewind(){
 * postion = 0;
 * mark = -1;
 * return this;
 * }
 * <p>
 * clear:清除缓冲区,将位置设置为0,将限制设置为容量,并丢弃标记
 * public final Buffer clear(){
 * postion = 0;
 * limit = capacity;
 * mark = -1;
 * return -1;
 * }
 * <p>
 * flip:反转缓冲区,首先将限制设置为当前位置,然后将位置设置为0.
 * public final Buffer flip(){
 * limit = postion;
 * postion = 0;
 * mark = -1;
 * return this;
 * }
 * <p>
 * 三者的侧重点:
 * rewind:侧重在重新,在重新读取、重新写入时使用
 * clear:侧重还原一切状态
 * flip:侧重在substring截取
 */
public class NIOTest10 {
    public static void main(String[] args) {
        byte[] byteArray = new byte[]{1, 2, 3, 4};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());

        byteBuffer.position(1);
        byteBuffer.limit(3);
        byteBuffer.mark();

        System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());

        byteBuffer.rewind();
        System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());

    }

}

运行结果:

代码语言:javascript
复制
capacity=4 limit=4 postion=0
capacity=4 limit=3 postion=1
capacity=4 limit=3 postion=0
代码语言:javascript
复制
/**
 * 获取偏移量 ArrayOffset
 * final int arrayOffset():返回此缓冲区的底层实现数组中的第一个缓冲区元素的偏移量
 * public final int arrayOffset(){
 * if(hb==null)
 * throw new UnsupportedOperationException();
 * if(isReadOnly)
 * throw new ReadOnlyBufferException();
 * return offset;
 * }
 */
public class NIOTest11 {
    public static void main(String[] args) {
        getArrayOffsetZero();
        getArrayOffset();
    }

    //测试结果永远都为0的情况
    private static void getArrayOffsetZero() {
        byte[] byteArray = new byte[]{1,2,3,4,5,6};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        System.out.println("byteBuffer.arrayOffset ="+byteBuffer.arrayOffset());
    }

    //测试不为0的情况,偏移是相对而言的
    private static void getArrayOffset() {
        byte[] byteArray = new byte[]{1,2,3,4,5,6};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        byteBuffer.position(5);
        ByteBuffer byteBuffer1 = byteBuffer.slice();
        System.out.println("byteBuffer.arrayOffset ="+byteBuffer1.arrayOffset());
    }

}

运行结果:

代码语言:javascript
复制
byteBuffer.arrayOffset =0
byteBuffer.arrayOffset =5
代码语言:javascript
复制
/**
 * 使用List.toArray(T[])
 */
public class NIOTest12 {
    public static void main(String[] args) {
        ByteBuffer buffer1 = ByteBuffer.wrap(new byte[]{'1','2','3','4'});
        ByteBuffer buffer2 = ByteBuffer.wrap(new byte[]{'c','d','t','v'});
        ByteBuffer buffer3 = ByteBuffer.wrap(new byte[]{'x','m','a','n'});
        List<ByteBuffer> list = new ArrayList<>();
        list.add(buffer1);
        list.add(buffer2);
        list.add(buffer3);

        ByteBuffer[] byteBufferArray = new ByteBuffer[list.size()];
        list.toArray(byteBufferArray);

        System.out.println(byteBufferArray.length);

        for (int i = 0; i<byteBufferArray.length;i++){
            ByteBuffer eachByteBuffer = byteBufferArray[i];
            while (eachByteBuffer.hasRemaining()){
                System.out.print((char)eachByteBuffer.get());
            }
            System.out.println();
        }
    }
}

运行结果:

代码语言:javascript
复制
3
1234
cdtv
xman
代码语言:javascript
复制
/**
 * 进行测试,包装wrap数据的处理
 */
public class NIOTest13 {
    public static void main(String[] args) {
        byte[] byteArray = new byte[]{1, 2, 3, 4, 5, 56};
        ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
        ByteBuffer byteBuffer1 = ByteBuffer.wrap(byteArray, 2, 4);
        System.out.println("byteBuffer capacity=" + byteBuffer.capacity() + " " + "postion=" + byteBuffer.position());
        System.out.println();
        System.out.println("byteBuffer1 capacity=" + byteBuffer1.capacity() + " " + "postion=" + byteBuffer1.position());
    }
}

运行结果:

代码语言:javascript
复制
byteBuffer capacity=6 postion=0
byteBuffer1 capacity=6 postion=2

了解完Buffer之后,我们就可以接着了解ByteBuffer和CharBuffer了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端技术学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档