美文网首页
Java-NIO(3)

Java-NIO(3)

作者: Cool_Pomelo | 来源:发表于2020-04-08 09:05 被阅读0次

    Java-NIO(3)

    ByteBuffer

    ByteBuffer为Buffer子类,可以在缓冲区中以字节为单位对数据进行存取

    此类定义了对字节缓冲区的六种操作类别:

    • 读取和写入单个字节的绝对和相对的get和put方法;

    • 相对批量获取方法,用于将字节的连续序列从此缓冲区传输到数组中;

    • 相对批量放置方法,用于将字节数组或某些其他字节缓冲区中连续的字节序列传输到此缓冲区中;

    • 绝对和相对的get和put方法,用于读取和写入其他基本类型的值,并将它们以特定字节顺序在字节序列之间来回转换;

    • 创建视图缓冲区的方法,该方法允许将字节缓冲区视为包含某些其他原始类型值的缓冲区;

    • 一种压缩字节缓冲区的方法。

    创建堆缓冲区与字节缓冲区

    字节缓冲区分两种:

    • 直接字节缓冲区

    • 非直接字节缓冲区

    针对直接字节缓冲区:

    JVM尽量在直接字节缓冲区上执行IO操作,直接对内核空间进行访问,提高运行效率

    allocateDirect与allocate

    源码:

    
    //新缓冲区的位置将为零,其极限将是其容量,其标记将是未定义的,其每个元素将被初始化为零,并且其字节顺序将为BIG_ENDIAN。 不确定是否具有支持数组。
    public static ByteBuffer allocateDirect(int capacity) {
            return new DirectByteBuffer(capacity);
    }
    
    
    //新缓冲区的位置将为零,其极限将是其容量,其标记将是未定义的,其每个元素将被初始化为零,并且其字节顺序将为BIG_ENDIAN。 它将有一个支持数组,其数组偏移量将为零。
    public static ByteBuffer allocate(int capacity) {
            if (capacity < 0)
                throw new IllegalArgumentException();
            return new HeapByteBuffer(capacity, capacity);
    }
    

    对比下allocate方法与wrap方法发现:

    • 前者是创建一个新的数组,而后者则是使用传入的数组作为存储空间

    • wrap()关联的数组进行操作会影响到缓冲区中的数据,而操作缓冲区中数据也会影响到与之关联的数组中数据,因为它们引用的是用一个数组对象

    
    public class T1 {
    
    
        public static void main(String[] args) {
    
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
            ByteBuffer byteBuffe2 = ByteBuffer.allocate(100);
    
            print(byteBuffer);
            System.out.println("-----------------------------------");
            print(byteBuffe2);
    
        }
    
        private static void print(Buffer buffer) {
    
            System.out.println("position : \n"
            + buffer.position()
            +"\n"
            +"limit : \n"
            +buffer.limit()
            +"\n"
            +buffer
            +"isDirect?\n"
            +buffer.isDirect());
    
        }
    }
    
    
    

    直接与非直接缓冲区运行效率比较

    
    
    public class T2 {
    
    
        public static void main(String[] args) {
    
    //        t1();//973
    //        t2();//1307
    
        }
    
        private static void t1(){
            long begin = System.currentTimeMillis();
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1000000000);
            for (int i = 0; i < 1000000000; i++) {
                byteBuffer.put((byte) 123);
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        }
    
        private static void t2(){
            long begin = System.currentTimeMillis();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1000000000);
            for (int i = 0; i < 1000000000; i++) {
                byteBuffer.put((byte) 123);
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        }
    }
    
    

    wrap数据的处理

    wrap​(byte[] array) & wrap​(byte[] array,int offset,int length)

    源码实现:

    
    //新缓冲区将由给定的字节数组支持; 也就是说,对缓冲区的修改将导致数组被修改,反之亦然。 新缓冲区的容量和限制将为array.length,其位置为零,其标记为未定义,其字节顺序为BIG_ENDIAN。 它的支持数组将是给定的数组,其数组偏移量将为零。
    public static ByteBuffer wrap(byte[] array) {
        return wrap(array, 0, array.length);
    }
    
    
    //新缓冲区将由给定的字节数组支持; 也就是说,对缓冲区的修改将导致数组被修改,反之亦然。 新缓冲区的容量为array.length,其位置为offset,其限制为offset + length,其标记为undefined,其字节顺序为BIG_ENDIAN。 它的支持数组将是给定的数组,其数组偏移量将为零。
    
    //Parameters:
    //array - 将支持新缓冲区的数组
    //offset - 要使用的子数组的偏移量; 必须为非负数,且不得大于array.length。 新缓冲区的位置将设置为此值。
    //length -length-要使用的子数组的长度; 必须为非负且不大于array.length-offset。 新缓冲区的限制将设置为偏移量+长度。
    
    public static ByteBuffer wrap(byte[] array,
                                    int offset, int length)
    {
        try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }
    
    
    public class T3 {
    
        public static void main(String[] args) {
    
            byte[] bytes = new byte[]{1,2,3,4,5,6,7,8,9};
    
            ByteBuffer b1 = ByteBuffer.wrap(bytes);
            ByteBuffer b2 = ByteBuffer.wrap(bytes,2,4);
    
            System.out.println("b1 capacity : \n"
            +b1.capacity()
            +"\n"
            +"b1 limit : \n"
            +b1.limit()
            +"\n"
                    +"b1 position : \n"
                    +b1.position());
    
            System.out.println("");
    
            System.out.println("b2 capacity : \n"
                    +b2.capacity()
                    +"\n"
                    +"b2 limit : \n"
                    +b2.limit()
                    +"\n"
                    +"b2 positon : \n"
                    +b2.position());
        }
    }
    
    

    put() & get()

    Buffer类每个子类都定义了两种get & put操作

    • 相对位置操作

    • 绝对位置操作

    相对位置操作:读取或写入元素时,从“当前位置开始”,然后将位置增加所传输的元素数量

    绝对位置操作:采取显式的元素索引,操作不影响位置

    
    //使用相对位置
    public abstract ByteBuffer put(byte b);
    
    
    
    public class T4 {
    
        public static void main(String[] args) {
    
            ByteBuffer b1 = ByteBuffer.allocate(10);
            print(b1,"初始");
    
            System.out.println("");
    
            b1.put((byte) 225);
            print(b1,"put之后");
    
            System.out.println("");
    
            b1.put((byte) 127);
            print(b1,"再次put之后");
    
            b1.rewind();
            
            print(b1,"rewind之后");
    
            System.out.println("");
    
            System.out.println(b1.get());
    
            print(b1,"get 之后");
    
            System.out.println("");
    
            System.out.println(b1.get());
    
            System.out.println("");
    
            print(b1,"再次get之后");
    
    
        }
    
        private static void print(Buffer buffer,String name) {
            System.out.println(name
            +"\n"
            +"capacity : \n"
            +buffer.capacity()
            +"\n"
            +"limit : \n"
            +buffer.limit()
            +"\n"
            +"position : \n"
            +buffer.position());
        }
    }
    
    
    

    执行相对位置读写之后,位置呈现递增状态,位置自动移动到下一个位置上

    put​(byte[] src,int offset,int length) & get​(byte[] dst,int offset,int length)

    源码实现:

    
    //相对批量放置方法(可选操作)。
    
    //此方法将字节从给定的源数组传输到此缓冲区中。 如果要从数组复制的字节多于该缓冲区中剩余的字节,即,如果length> missing(),则不传输任何字节,并抛出BufferOverflowException。
    
    //否则,此方法从给定数组的长度字节复制到此缓冲区,从数组中的给定偏移量开始,并从该缓冲区的当前位置开始。 然后,该缓冲区的位置将增加长度。
    
    //换句话说,以dst.put(src,off,len)形式调用此方法具有与循环完全相同的效果。
    
    //for (int i = off; i < off + len; i++)
             //dst.put(a[i]);
    
    //除了首先检查此缓冲区中是否有足够的空间,而且它的效率可能要高得多。
    
    //Parameters:
    
    //src-从中读取字节的数组
    //offset-要读取的第一个字节在数组中的偏移量; 必须为非负且不大于array.length
    //length-要从给定数组中读取的字节数; 必须为非负且不大于array.length-offset
    public ByteBuffer put(byte[] src, int offset, int length) {
        checkBounds(offset, length, src.length);
        if (length > remaining())
            throw new BufferOverflowException();
        int end = offset + length;
        for (int i = offset; i < end; i++)
            this.put(src[i]);
        return this;
    }
    
    
    public class T {
    
        public static void main(String[] args) {
    
            byte[] bytes = {1,2,3,4,5,6,7,8,9};
            byte[] bytes2 = {11,22,33,44,55,66,77};
    
            ByteBuffer b1 = ByteBuffer.allocate(10);
            b1.put(bytes);
            System.out.println(b1.position());
            b1.position(2);
    
            b1.put(bytes2,1,3);
    
            System.out.println("A");
            byte[] getB = b1.array();
            for (int i = 0; i < getB.length ; i++) {
                System.out.print(getB[i] + "  ");
            }
            System.out.println("");
            System.out.println(b1.position());
            System.out.println("");
            b1.position(1);
            byte[] out = new byte[b1.capacity()];
            b1.get(out,3,4);
            System.out.println("B");
            for (int i = 0; i < out.length; i++) {
                System.out.print(out[i] + "   ");
            }
        }
    }
    
    

    图示:

    图1.png 图2.png
      public static void main(String[] args) {
    
            byte[] bytes = {1,2,3,4,5,6,7,8,9};
    
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    
            int index = 0;
    
            while ( index < bytes.length) {
    
                int readLen = Math.min(
                        byteBuffer.remaining(),
                        bytes.length - index
                );
    
                byteBuffer.put(bytes,index,readLen);
    
                byteBuffer.flip();
    
                byte[] getB = byteBuffer.array();
    
                for (int i = 0; i < byteBuffer.limit(); i++) {
                    System.out.print(getB[i] + "  ");
                }
                index = index + readLen;
    
                System.out.println("");
    
                byteBuffer.clear();
    
            }
        }
    
    

    上面代码在byte[]的length大于或等于缓冲区的remaining(),或者小于等于remaining()时都可以正确的运行

    put​(byte[] src) & get(byte[] dst)

    文档说明:

    //相对批量放置方法(可选操作)。
    //此方法将给定源字节数组的全部内容传输到此缓冲区中。 以dst.put(a)形式调用此方法的行为与调用的行为完全相同
    //     dst.put(a, 0, a.length) 
    
    final ByteBuffer put​(byte[] src)
    
    //相对批量获取方法。
    //此方法将字节从此缓冲区传输到给定的目标数组。 形式为src.get(a)的此方法的调用与该调用的行为完全相同
    //     src.get(a, 0, a.length) 
    ByteBuffer get​(byte[] dst)
    
    
    public class T2 {
    
        public static void main(String[] args) {
    
            byte[] bytes = new byte[]{1,2,3,4,5};
    
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    
            byteBuffer.put((byte) 1);
            byteBuffer.put((byte) 2);
    
            System.out.println("position : \n" + byteBuffer.position() + "\n");
    
            byteBuffer.put(bytes);
    
            System.out.println("position : \n" + byteBuffer.position() + "\n");
    
            byteBuffer.flip();
    
            byteBuffer.position(3);
    
            System.out.println("position : \n" + byteBuffer.position() + "\n");
            byte[] newB= new byte[byteBuffer.remaining()];
    
            byteBuffer.get(newB);
    
            for (int i = 0; i < newB.length; i++) {
                System.out.print(newB[i] + "  ");
    
            }
        }
    }
    
    
    

    put​(int index,byte b) & get​(int index)

    文档说明:

    
    //绝对放置方法(可选操作)。
    //将给定字节以给定索引写入此缓冲区。
    abstract ByteBuffer put​(int index,byte b)
    
    //绝对获取方法。 读取给定索引处的字节。
    abstract byte get​(int index)
    

    这两个操作都属于绝对位置操作,位置position不会发生改变

    public class T3 {
    
        public static void main(String[] args) {
    //        test1();
            test2();
        }
    
        private static void test1() {
    
            byte[] bytes = {1,2,3,4,5};
            ByteBuffer buffer = ByteBuffer.allocate(10);
            buffer.put(bytes);
            buffer.put(2, (byte) 123);
            System.out.println(buffer.get(2));
            buffer.position(0);
            byte[] bytes1 = new byte[buffer.capacity()];
            buffer.get(bytes1,0,bytes1.length);
            for (int i = 0; i < bytes1.length; i++) {
                System.out.print(bytes1[i] + "  ");
            }
        }
    
        private static void test2() {
    
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    
            byteBuffer.position(9);
    
            System.out.println(byteBuffer.position());
    
            byteBuffer.put(2, (byte) 127);
            System.out.println(byteBuffer.position());
    
            byteBuffer.rewind();
    
            byte[] out = new byte[byteBuffer.capacity()];
    
            byteBuffer.get(out,0,out.length);
    
            for (int i = 0; i < out.length; i++) {
                System.out.print(out[i] + "  " );
            }
        }
    }
    
    

    put​(ByteBuffer src)

    JDK:

    
    //相对批量放置方法(可选操作)。
    //此方法将给定源缓冲区中剩余的字节传输到此缓冲区中。 如果源缓冲区中剩余的字节数大于此缓冲区中的字节数,即if src.remaining() > remaining(),则不传输任何字节,并抛出BufferOverflowException。
    //否则,此方法从每个缓冲区的当前位置开始,将n = src.remaining()字节从给定缓冲区复制到此缓冲区中。 然后将两个缓冲区的位置加n。
    //换句话说,以dst.put(src)形式调用此方法具有与循环完全相同的效果。
    // while (src.hasRemaining())
             //dst.put(src.get()); 
    //除了首先检查此缓冲区中是否有足够的空间,而且它的效率可能要高得多。
    public ByteBuffer put(ByteBuffer src) {
        if (src == this)
            throw new IllegalArgumentException();
        if (isReadOnly())
            throw new ReadOnlyBufferException();
        int n = src.remaining();
        if (n > remaining())
            throw new BufferOverflowException();
        for (int i = 0; i < n; i++)
            put(src.get());
        return this;
    }
    
    public class T4 {
    
        public static void main(String[] args) {
    
            byte[] bs = {1,2,3,4,5,6,7,8};
    
            ByteBuffer b1 = ByteBuffer.wrap(bs);
    
            byte[] bs2 = {55,66,77};
    
            ByteBuffer b2 = ByteBuffer.wrap(bs2);
    
            b1.position(4);
            b2.position(1);
    
            b1.put(b2);
    
            System.out.println("b1改变 " +b1.position() + "\n");
            System.out.println("b2改变 " +b2.position() + "\n");
    
            byte[] out = b1.array();
    
            for (int i = 0; i < out.length; i++) {
                System.out.print(out[i] + "  ");
            }
        }
    
    
    }
    
    
    

    putType() & getType()

    JDK:

    
    
    
    
    //读取char值的相对get方法。
    //读取此缓冲区当前位置的下两个字节,根据当前字节顺序将它们组成一个char值,然后将该位置加2。
    abstract char getChar()
    
    
    //用于写入char值的相对put方法(可选操作)。
    //以当前字节顺序将包含给定char值的两个字节写入当前位置的此缓冲区中,然后将该位置加2。
    abstract ByteBuffer putChar​(char value)
    
    
    
    //用于读取char值的绝对get方法。
    //读取给定索引处的两个字节,根据当前字节顺序将它们组成一个char值。
    abstract char getChar​(int index)
    
    
    //用于写入char值的绝对put方法(可选操作)。
    //将包含当前char值的给定char值的两个字节按给定索引写入此缓冲区。
    abstract ByteBuffer putChar​(int index,char value)
    
    
    

    其余基本类型操作与上面类似

    
    public class T5 {
    
    
        public static void main(String[] args) {
    
            ByteBuffer b1 = ByteBuffer.allocate(100);
            b1.putChar('a'); // 0 - 1 char 占两个字节
    
            b1.putChar(2,'b'); // 2-3
    
            b1.position(4);
    
            b1.putDouble(1.1);// 4 - 11 double 占8个字节
    
            b1.putDouble(12,1.2);//12 - 19
    
            b1.position(20);
            b1.putFloat(2.1F);//20 - 23 float占4个字节
            b1.putFloat(24,2.6F);
    
            b1.position(28);
            b1.putInt(25);// 28-31 int 4 个字节
            b1.putInt(32,32);//32-35
    
            b1.position(36);
            b1.putLong(41L);//36-43 long 8 字节
            b1.putLong(44,45L);
    
            b1.position(52);
            b1.putShort((short) 51);// 52 -53 short 2 字节
            b1.putShort(54, (short) 56);// 54 -55
    
    
            b1.position(0);
    
            byte[] out = b1.array();
    
            for (int i = 0; i < out.length; i++) {
                System.out.print(out[i] + "  ");
            }
    
            System.out.println("");
    
            System.out.println(b1.getChar());
            System.out.println(b1.getChar(2));
    
        }
    }
    
    
    

    slice() & arrayOffSet()

    JDK:

    
    
    
    //创建一个新的字节缓冲区,其内容是该缓冲区内容的共享子序列。
    //新缓冲区的内容将从该缓冲区的当前位置开始。 对该缓冲区内容的更改将在新缓冲区中可见,反之亦然; 两个缓冲区的位置,限制和标记值将是独立的。
    //新缓冲区的位置将为零,其容量和限制将为该缓冲区中剩余的字节数,其标记将为未定义,其字节顺序为BIG_ENDIAN。 当且仅当该缓冲区是直接缓冲区时,新缓冲区才是直接缓冲区;当且仅当该缓冲区是只读缓冲区时,新缓冲区才是只读缓冲区。
    //Returns:
    //The new byte buffer
    public abstract ByteBuffer slice()
    
    
    //返回此缓冲区的第一个元素在此缓冲区的后备数组内的偏移量(可选操作)。
    //如果此缓冲区由数组支持,则缓冲区位置p对应于数组索引p + arrayOffset()。
    //在调用此方法之前,请先调用hasArray方法,以确保此缓冲区具有可访问的后备数组。
    //返回值:
    //该缓冲区的数组中第一个元素的偏移量
    public final int arrayOffset()
    
    
    
    public class T6 {
    
        public static void main(String[] args) {
    
            byte[] bytes1 = {1,2,3,4,5,6,7,8};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(bytes1);
    
            buffer1.position(5);
    
            ByteBuffer buffer2 = buffer1.slice();
    
            System.out.println("bu1 position = "
            + buffer1.position()
            +"\n"
            +"bu1 capacity "
            +buffer1.capacity()
            +"\n"
            +"bu1 limit "
            + buffer1.limit());
    
            System.out.println("---------------------------------");
    
            System.out.println("bu2 position = "
                    + buffer2.position()
                    +"\n"
                    +"bu2 capacity "
                    +buffer2.capacity()
                    +"\n"
                    +"bu2 limit "
                    + buffer2.limit());
    
            System.out.println("");
    
    
            buffer2.put(0, (byte) 11);
    
            byte[] bs1 = buffer1.array();
    
            byte[] bs2 = buffer2.array();
    
            for (int i = 0; i < bs1.length; i++) {
                System.out.print(bs1[i] + "  ");
            }
    
            System.out.println("");
    
            for (int i = 0; i < bs2.length; i++) {
                System.out.print(bs2[i] + "  ");
            }
    
    
    
        }
    }
    
    

    转换为CharBuffer字符缓冲区 & 中文处理

    JDK:

    
    
    //创建此字节缓冲区的视图作为char缓冲区。
    //新缓冲区的内容将从该缓冲区的当前位置开始。 对该缓冲区内容的更改将在新缓冲区中可见,反之亦然; 两个缓冲区的位置,限制和标记值将是独立的。
    //新缓冲区的位置将为零,其容量和限制将是该缓冲区中剩余的字节数除以2,其标记将是未定义的,并且其字节顺序将是视图出现时的字节缓冲区的顺序。 创建。 当且仅当该缓冲区是直接缓冲区时,新缓冲区才是直接缓冲区;当且仅当该缓冲区是只读缓冲区时,新缓冲区才是只读缓冲区。
    public abstract CharBuffer asCharBuffer()
    
    
    
    
    public class T7 {
    
        public static void main(String[] args) {
    
            byte[] bs1 = "我是中国人".getBytes();
    
            // 运行本代码的*.java文件是UTF-8编码,所以运行环境取得的编码默认是UTF-8
            System.out.println(Charset.defaultCharset().name());
    
            System.out.println("");
    
            ByteBuffer b1 = ByteBuffer.wrap(bs1);
            System.out.println("bytebuffer =   " + b1.getClass().getName());
    
            System.out.println("");
    
            CharBuffer charBuffer = b1.asCharBuffer();
    
            System.out.println("charbuffer   " + charBuffer.getClass().getName());
    
            System.out.println();
    
            System.out.println("b1 position   "
            + b1.position()
            +"\n"
            +"b1 capacity  "
            + b1.capacity()
            +"\n"
            +"b1 limit   "
            +b1.limit());
    
            System.out.println("");
    
            System.out.println("c1 position   "
                    + charBuffer.position()
                    +"\n"
                    +"c1 capacity  "
                    + charBuffer.capacity()
                    +"\n"
                    +"c1 limit   "
                    +charBuffer.limit());
    
            System.out.println("");
    
            System.out.println(charBuffer.capacity());
    
            charBuffer.position(0);
    
            for (int i = 0; i < charBuffer.capacity(); i++) {
                
                // get()方法时使用的编码 Wie UTF-16BE
                // 所以乱码了
                System.out.print(charBuffer.get() + "  ");
            }
    
    
        }
    }
    
    

    上面代码中的四个步骤:

    • byte[] bs1 = "我是中国人".getBytes();将中文转成字节数组,数组中存储的编码为UTF-8

    • ByteBuffer b1 = ByteBuffer.wrap(bs1);将UTF-8编码的字节数组转换成字节缓冲区,缓冲区中存储的编码也为UTF-8

    • CharBuffer charBuffer = b1.asCharBuffer();将编码格式为UTF-8的ByteBuffer中的内容转换成UTF-8编码的CharBuffer

    • 调用CharBuffer子类java.nio.ByteBufferAsCharBufferB中get方法,以UTF-16BE的编码格式获得中文时出现编码不匹配情况,所以乱码了

    解决方案;

    
    public class T8 {
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            byte[] bs1 = "我是中国人".getBytes("utf-16BE");
    
            System.out.println(Charset.defaultCharset().name());
    
            System.out.println("");
    
            ByteBuffer b1 = ByteBuffer.wrap(bs1);
            System.out.println("bytebuffer =   " + b1.getClass().getName());
    
            System.out.println("");
    
            CharBuffer charBuffer = b1.asCharBuffer();
    
            System.out.println("charbuffer   " + charBuffer.getClass().getName());
    
            System.out.println();
    
            System.out.println("b1 position   "
                    + b1.position()
                    +"\n"
                    +"b1 capacity  "
                    + b1.capacity()
                    +"\n"
                    +"b1 limit   "
                    +b1.limit());
    
            System.out.println("");
    
            System.out.println("c1 position   "
                    + charBuffer.position()
                    +"\n"
                    +"c1 capacity  "
                    + charBuffer.capacity()
                    +"\n"
                    +"c1 limit   "
                    +charBuffer.limit());
    
            System.out.println("");
    
            System.out.println(charBuffer.capacity());
    
            charBuffer.position(0);
    
            for (int i = 0; i < charBuffer.capacity(); i++) {
                System.out.print(charBuffer.get() + "  ");
            }
    
        }
    }
    
    

    另一种方案;

    
    public class T9 {
    
        public static void main(String[] args) throws UnsupportedEncodingException {
    
            byte[] bs1 = "我是中国人".getBytes("utf-8");
    
            System.out.println(Charset.defaultCharset().name());
    
            ByteBuffer buffer = ByteBuffer.wrap(bs1);
            CharBuffer charBuffer = Charset.forName("utf-8").decode(buffer);
    
            charBuffer.position(0);
    
            for (int i = 0; i < charBuffer.limit(); i++) {
                System.out.print(charBuffer.get() + "  ");
            }
    
    
        }
    }
    

    对于ByteBuffer的更改会直接影响到CharBuffer:

    
    public class T11 {
    
        public static void main(String[] args) {
    
            byte[] bytes = "abcdeff".getBytes();
    
            ByteBuffer buffer = ByteBuffer.wrap(bytes);
            CharBuffer charBuffer = buffer.asCharBuffer();
    
            for (int i = 0; i < charBuffer.limit(); i++) {
                System.out.print(charBuffer.get() + "     "    );
    
            }
    
            System.out.println("");
            buffer.put((byte) 2);
    
            buffer.put((byte) 45);
    
            charBuffer.clear();
    
            for (int i = 0; i < charBuffer.limit(); i++) {
                System.out.print(charBuffer.get() + "     "    );
            }
    
        }
    }
    
    

    设置&获得字节顺序

    JDK:

    
    //检索此缓冲区的字节顺序。
    //在读取或写入多字节值以及创建作为此字节缓冲区视图的缓冲区时,将使用字节顺序。 新创建的字节缓冲区的顺序始终为BIG_ENDIAN。
    public final ByteOrder order()
    
    

    public static final ByteOrder BIG_ENDIAN:表示big-endian字节顺序的常数。 按照此顺序,多字节值的字节按从最高有效到最低有效的顺序排列。

    public static final ByteOrder LITTLE_ENDIAN:表示little-endian字节顺序的常数。 按此顺序,多字节值的字节从最低有效到最高有效。

    
    public class T12 {
    
        public static void main(String[] args) {
    
            int val = 123456789;
    
            ByteBuffer buffer1 = ByteBuffer.allocate(4);
    
            System.out.println(buffer1.order() + "  ");
    
            System.out.println(buffer1.order() + "  " );
    
            buffer1.putInt(val);
    
            byte[] bs = buffer1.array();
    
            for (int i = 0; i < bs.length; i++) {
                System.out.print(bs[i] + "  ");
            }
    
            System.out.println("");
    
            buffer1 = ByteBuffer.allocate(4);
    
            System.out.println(buffer1.order() + "  ");
    
            buffer1.order(ByteOrder.BIG_ENDIAN);
    
            System.out.println(buffer1.order() + " ");
    
            buffer1.putInt(val);
    
            bs = buffer1.array();
    
            for (int i = 0; i < bs.length; i++) {
                System.out.print(bs[i] + "  ");
            }
    
            System.out.println("");
    
            buffer1 = ByteBuffer.allocate(4);
    
            System.out.println(buffer1.order() + "  ");
    
            buffer1.order(ByteOrder.LITTLE_ENDIAN);
    
            System.out.println(buffer1.order() + " ");
    
            buffer1.putInt(val);
    
            bs = buffer1.array();
    
            for (int i = 0; i < bs.length; i++) {
                System.out.print(bs[i] + " ");
            }
        }
    }
    
    
    

    创建只读缓冲区

    JDK:

    
    //创建一个共享该缓冲区内容的新的只读字节缓冲区。
    //新缓冲区的内容就是该缓冲区的内容。 对该缓冲区内容的更改将在新缓冲区中可见。 但是,新缓冲区本身将是只读的,并且不允许修改共享内容。 这两个缓冲区的位置,限制和标记值将是独立的。
    //新缓冲区的容量,限制,位置和标记值将与此缓冲区的容量,限制,位置和标记值相同,并且其字节顺序为BIG_ENDIAN。
    //如果此缓冲区本身是只读的,则此方法的行为与重复方法完全相同。
    public abstract ByteBuffer asReadOnlyBuffer()
    
    
    
    
    public class T13 {
    
        public static void main(String[] args) {
    
            byte[] bs = {1,2,3,4,5,6,7,8};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(bs);
            ByteBuffer read_buffer = buffer1.asReadOnlyBuffer();
    
            System.out.println("is readOnly \n"
            + buffer1.isReadOnly());
    
            System.out.println("is readOnly \n"
                    + read_buffer.isReadOnly());
    
            read_buffer.rewind();
    
            read_buffer.put((byte) 123);
            
        }
    }
    
    

    压缩缓冲区

    JDK:

    //压缩此缓冲区(可选操作)。
    //缓冲区当前位置与其限制之间的字节(如果有)被复制到缓冲区的开头。 也就是说,将索引p = position()的字节复制到索引零,将索引p + 1的字节复制到索引1,依此类推,直到索引limit()-1的字节复制到索引n = limit()-1-页 然后将缓冲区的位置设置为n + 1,并将其限制设置为其容量。 如果定义了该标记,则将其丢弃。
    //缓冲区的位置设置为要复制的字节数,而不是零,因此,在调用此方法之后,可以立即调用另一个相对的put方法。
    //从缓冲区写入数据后,如果写入未完成,请调用此方法。 例如,以下循环通过缓冲区buf将字节从一个通道复制到另一个通道:
    //   buf.clear();          // Prepare buffer for use
    //    while (in.read(buf) >= 0 || buf.position != 0) {
    //        buf.flip();
    //        out.write(buf);
    //        buf.compact();    // In case of partial write
    //    }
    public abstract ByteBuffer compact()
    
    

    图示:

    图3.png
    public class T14 {
    
        public static void main(String[] args) {
    
            ByteBuffer buffer1 = ByteBuffer.wrap(new byte[]{1,2,34,5,6});
    
            System.out.println("capacity : \n"
            + buffer1.capacity()
            +"\n"
            +"position: \b"
            +buffer1.position()
            +"\n"
            +"limit : \n"
            +buffer1.limit());
    
            System.out.println("------------------------------");
    
            System.out.println("getval : \n" + buffer1.get());
    
            System.out.println("------------------------------");
    
            System.out.println("capacity : \n"
                    + buffer1.capacity()
                    +"\n"
                    +"position: \b"
                    +buffer1.position()
                    +"\n"
                    +"limit : \n"
                    +buffer1.limit());
    
            System.out.println("------------------------------");
    
            System.out.println("getVal : \n" + buffer1.get());
    
            System.out.println("------------------------------");
    
            buffer1.compact();
    
            System.out.println("compact");
    
            System.out.println("capacity : \n"
                    + buffer1.capacity()
                    +"\n"
                    +"position: \b"
                    +buffer1.position()
                    +"\n"
                    +"limit : \n"
                    +buffer1.limit());
    
            byte[] out = buffer1.array();
    
            for (int i = 0; i < out.length ; i++) {
                System.out.print(out[i] + "  ");
            }
    
    
    
        }
    }
    
    
    

    比较缓冲区内容

    equals() & compareTo()

    JDK:

    
    //区分此缓冲区是否等于另一个对象。
    //当且仅当两个字节缓冲区相等时满足以下条件:
    
    //它们具有相同的元素类型,
    //它们具有相同数量的剩余元素,并且
    //独立于其起始位置考虑的其余元素的两个序列在点上相等。
    
    
    public boolean equals(Object ob) {
        if (this == ob)
            return true;
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
            if (!equals(this.get(i), that.get(j)))
                return false;
        return true;
    }
    
    

    通过源码,可以看出两个缓冲区的capacity可以不一样,说明equals比较的是position以及limit的内容是否一致

    
    public class T1 {
    
        public static void main(String[] args) {
    
    //        test1();
    //        test2();
    //        test3();
            test4();
        }
    
    /*
    判断是不是自身,如果是 返回true
     */
        private static void test2() {
            byte[] bs = {1,2,3,4,5};
            ByteBuffer buffer = ByteBuffer.wrap(bs);
    
            System.out.println(buffer.equals(buffer));
    
        }
    
        /*
        验证是不是ByteBuffer类的实例 不是返回false
         */
        private static void test1() {
    
            byte[] bs = {1,2,3,4,5};
            int[] ints = {1,2,3,4,5};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(bs);
            IntBuffer buffer2 = IntBuffer.wrap(ints);
    
            System.out.println(buffer1.equals(buffer2));
    
        }
    
    
        /*
        判断remainging是否一样 不一样返回false
         */
        private static void test3() {
            byte[] b1 = {3,4,5};
            byte[] b2 = {1,2,3,4,5,6,7,8};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(b1);
            ByteBuffer buffer2 = ByteBuffer.wrap(b2);
    
            buffer1.position(0);
            buffer2.position(3);
    
            System.out.println(buffer1.equals(buffer2));
            System.out.println("");
            System.out.println(buffer1.remaining());
            System.out.println(buffer2.remaining());
    
        }
    
        /*
        验证 判断两个缓冲区中的 position以及limit之间的数据是否完全一样
        只要有一个字节不一样  就返回false 
         */
        private static void test4() {
    
            byte[] b1 = {3,4,5};
            byte[] b2 = {1,2,3,4,5,6,7,8};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(b1);
            ByteBuffer buffer2 = ByteBuffer.wrap(b2);
    
            buffer1.position(0);
            buffer1.limit(3);
            buffer2.position(2);
            buffer2.limit(5);
    
            System.out.println(buffer1.equals(buffer2));
    
            System.out.println("");
    
            System.out.println(buffer1.remaining());
            System.out.println(buffer2.remaining());
    
            System.out.println("");
    
            buffer2.put(3, (byte) 44);
    
            System.out.println("");
    
            System.out.println(buffer1.equals(buffer2));
    
    
            System.out.println(buffer1.remaining());
            System.out.println(buffer2.remaining());
        }
        
    }
    
    

    JDK:

    
    
    //将此缓冲区与另一个缓冲区进行比较。
    //通过按字典顺序比较两个字节缓冲区的其余元素序列来比较两个字节缓冲区,而不考虑每个序列在其相应缓冲区中的开始位置。 对字节元素进行比较,就好像通过调用Byte.compare(byte,byte)一样。
    //字节缓冲区无法与任何其他类型的对象进行比较。
    public int compareTo(ByteBuffer that) {
        int n = this.position() + Math.min(this.remaining(), that.remaining());
        for (int i = this.position(), j = that.position(); i < n; i++, j++) {
            int cmp = compare(this.get(i), that.get(j));
            if (cmp != 0)
                return cmp;
        }
        return this.remaining() - that.remaining();
    }
    
    
    
    
    public class T2 {
    
        public static void main(String[] args) {
    
    //        test1();//-3
    
            test2();//-5
            
            
    
        }
    
        /*
        验证  如果开始与结束的范围之间有一个字节不一样  则返回两者的减数
         */
        private static void test1() {
            byte[] b1 = {3,4,5};
            byte[] b2 = {1,2,3,4,5,6,7,8};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(b1);
            ByteBuffer buffer2 = ByteBuffer.wrap(b2);
    
            buffer1.position(0);
            buffer2.position(2);
    
            System.out.println(buffer1.compareTo(buffer2));
        }
    
        /*
        验证  如果开始与结束之间每个字节一样  则返回两者remaining的减数
         */
        private static  void test2() {
    
            byte[] b1 = {3,4,5};
            byte[] b2 = {1,2,3,4,5,6,7,8,9,10};
    
            ByteBuffer buffer1 = ByteBuffer.wrap(b1);
            ByteBuffer buffer2 = ByteBuffer.wrap(b2);
    
            buffer1.position(0);
            buffer2.position(2);
    
            System.out.println(buffer1.compareTo(buffer2));
        }
    }
    
    

    复制缓冲区

    JDK:

    
    //创建一个共享该缓冲区内容的新字节缓冲区。
    //新缓冲区的内容就是该缓冲区的内容。 对该缓冲区内容的更改将在新缓冲区中可见,反之亦然; 两个缓冲区的位置,限制和标记值将是独立的。
    //新缓冲区的容量,限制,位置和标记值将与此缓冲区的容量,限制,位置和标记值相同,并且其字节顺序为BIG_ENDIAN。 当且仅当该缓冲区是直接缓冲区时,新缓冲区才是直接缓冲区;当且仅当该缓冲区是只读缓冲区时,新缓冲区才是只读缓冲区。
    public abstract ByteBuffer duplicate()
    
    
    
    public class T3 {
    
        public static void main(String[] args) {
    
            byte[] bs = {1,2,3,4,5};
            ByteBuffer buffer1 = ByteBuffer.wrap(bs);
            buffer1.position(2);
    
            System.out.println("  1   catacity   limit    position \n");
            System.out.println(buffer1.capacity()+"    " + buffer1.limit()+"   "
            +buffer1.position());
    
            System.out.println("");
    
            ByteBuffer buffer2 = buffer1.slice();
            ByteBuffer buffer3 = buffer1.duplicate();
    
            ByteBuffer buffer4 = buffer1;
    
            System.out.println("2   catacity   limit    position \n");
            System.out.println(buffer2.capacity()+"    " + buffer2.limit()+"   "
                    +buffer2.position());
    
            System.out.println("");
    
    
            System.out.println("3   catacity   limit    position \n");
            System.out.println(buffer3.capacity()+"    " + buffer3.limit()+"   "
                    +buffer3.position());
    
            System.out.println("");
    
            buffer2.position(0);
    
            for (int i = buffer2.position(); i < buffer2.limit(); i++) {
                System.out.print(buffer2.getChar(i) + "  ");
            }
    
            System.out.println("");
    
            buffer3.position(3);
    
            for (int i = buffer3.position(); i < buffer3.limit(); i++) {
                System.out.print(buffer3.getChar(i) + "  ");
            }
    
        }
    }
    
    

    缓冲区扩容

    一旦创建缓冲区,容量就不能被改变了,如果想对缓冲区进行扩展,就要进行相应的处理

    
    public class T4 {
    
        public static void main(String[] args) {
    
            byte[] bs = {1,2,3,4,5};
            ByteBuffer buffer = ByteBuffer.wrap(bs);
            ByteBuffer buffer12 = extendsSize(buffer,2);
            byte[] out = buffer12.array();
            for (int i = 0; i < out.length; i++) {
                System.out.print(out[i] + "  ");
            }
    
        }
    
        public static ByteBuffer extendsSize(ByteBuffer byteBuffer,int extendsSize) {
            ByteBuffer newbuffer = ByteBuffer.allocate(byteBuffer.capacity() + extendsSize);
            newbuffer.put(byteBuffer);
            return newbuffer;
        }
    
    
    }
    
    
    

    参考资料

    《NIO与Socket编程技术指南》

    相关文章

      网友评论

          本文标题:Java-NIO(3)

          本文链接:https://www.haomeiwen.com/subject/kxlzphtx.html