NIO学习笔记(三)ByteBuffer其他API

NIO学习笔记(三)ByteBuffer其他API

1、slice()方法的使用与arrayOffset()为非0测试

slice()方法的作用:创建新的字节缓冲区,其内容是此缓冲区内容的共享子序列。新缓冲区的内容将从此缓冲区的当前位置开始。此缓冲区的内容的更改在新缓冲区中是可见的;但是这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置将为0,其容量和限制将为此缓冲区中剩余的字节数,其标记是不确定的。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。

public class SliceDemo {
    public static void main(String[] args) {
        byte[] array1 = {1,2,3,4,5,6,7,8};
        ByteBuffer buffer = ByteBuffer.wrap(array1);
        buffer.position(5);

        ByteBuffer slice = buffer.slice();
        System.out.println("buffer1 capacity = " + buffer.capacity() +
                " limit="+buffer.limit()+" position="+buffer.position());
        System.out.println("slice capacity = " + slice.capacity() +
                " limit="+slice.limit()+" position="+slice.position());
        
        System.out.println(Arrays.toString(buffer.array()));
        System.out.println(Arrays.toString(slice.array()));
    }
}

在使用slice()方法后,在调用arrayOffset方法会出现返回的值为非0的情况。测试代码:

public class AraayOffsetDemo {
    public static void main(String[] args) {
        byte[] array1 = {1,2,3,4,5,6,7,8};
        ByteBuffer buffer = ByteBuffer.wrap(array1);
        buffer.position(5);
        ByteBuffer buffer1 = buffer.slice();
        System.out.println(buffer1.arrayOffset());
    }
}

运行结果说明buffer1的第一个元素的位置是相对于array1数组中的索引值为5偏移的。

2、bytebuffer转换为charbuffer字符缓冲区及中文处理

asCharBuffer()方法的作用:创建此字节缓冲区的视图,作为char缓冲区。新缓冲区的内容将从此缓冲区的当前位置开始。此缓冲区的内容更新在新缓冲区是可见的;这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置将为0,其容量和限制将为此缓冲区所剩字节的1/2,其标记是不确定的。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。

接下来看个例子:

public class AsCharBufferDemo {
    public static void main(String[] args) {
        byte[] array1 = "有情铁手".getBytes();
        //查看默认的编码格式
        System.out.println(Charset.defaultCharset().name());

        ByteBuffer buffer = ByteBuffer.wrap(array1);
        System.out.println("byteBuffer="+buffer.getClass().getName());

        CharBuffer charBuffer = buffer.asCharBuffer();
        System.out.println("charBuffer="+charBuffer.getClass().getName());

        System.out.println("buffer capacity = " + buffer.capacity() +
                " limit="+buffer.limit()+" position="+buffer.position());
        System.out.println("charBuffer capacity = " + charBuffer.capacity() +
                " limit="+charBuffer.limit()+" position="+charBuffer.position());
        System.out.println(charBuffer.capacity());
        charBuffer.position(0);
        for (int i = 0; i < charBuffer.capacity(); i++) {
            //这里会乱码,因为get方法采用的是UTF-16BE编码
            //而默认的是UTF-8编码
            //所以这里会乱码
            System.out.print(charBuffer.get()+"");
        }
        
    }
}

运行上述程序会出现乱码的问题

只需要稍微修改一下就可以解决乱码的问题:

public class AsCharBufferDemo {
    public static void main(String[] args) {
        byte[] array1 = "有情铁手".getBytes("UTF-16BE");
        //查看默认的编码格式
        System.out.println(Charset.defaultCharset().name());

        ByteBuffer buffer = ByteBuffer.wrap(array1);
        System.out.println("byteBuffer="+buffer.getClass().getName());

        CharBuffer charBuffer = buffer.asCharBuffer();
        System.out.println("charBuffer="+charBuffer.getClass().getName());

        System.out.println("buffer capacity = " + buffer.capacity() +
                " limit="+buffer.limit()+" position="+buffer.position());
        System.out.println("charBuffer capacity = " + charBuffer.capacity() +
                " limit="+charBuffer.limit()+" position="+charBuffer.position());
        System.out.println(charBuffer.capacity());
        charBuffer.position(0);
        for (int i = 0; i < charBuffer.capacity(); i++) {
            //这里会乱码,因为get方法采用的是UTF-16BE编码
            //而默认的是UTF-8编码
            //所以这里会乱码
            System.out.print(charBuffer.get()+"");
        }
        
    }
}

当然代码还可以这样修改

public class AsCharBufferDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        byte[] array1 = "有情铁手".getBytes("utf-8");
        //查看默认的编码格式
        System.out.println(Charset.defaultCharset().name());

        ByteBuffer buffer = ByteBuffer.wrap(array1);
        System.out.println("byteBuffer="+buffer.getClass().getName());

        CharBuffer charBuffer = Charset.forName("utf-8").decode(buffer);
        System.out.println("charBuffer="+charBuffer.getClass().getName());

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

    }
}

3、设置和获得字节顺序(大端模式,小端模式)

order()方法与字节排列顺序有关,因为不同的CPU读取字节时的顺序是不一样的,有的CPU从高位开始读取,而有的CPU从低位开始读取,当两种CPU传递数据时就是要将字节排列的顺序进行统一,此时order(ByteOrder bo) 方法就有了用武之地,它的作用就是设置字节的排列顺序。

那么什么是高位,什么是低位呢?如果是16位(双字节)的数据,如FF1A,高位就是FF,低位就是1A。如果是32位的数据,如3F68415B,高位字节就是3F68,低位字节就是415B,右边是低位,左边是高位。

ByteOrder order()方法的作用:获取此缓冲区的字节顺序。新创建的字节缓冲区的顺序始终为BIG_ENDIAN。在读取多字节以及以此字节缓冲区创建视图缓冲区时,使用该字节顺序。

  1. public static final ByteOrder BIG_ENDIAN:表示BIG_ENDIAN字节顺序的常量。按照此顺序,多字节值的字节顺序是从最高有效位到最低有效位的。也就是我们所说的大端模式。
  2. public static final ByteOrder LITTLE_ENDIAN:表示LITTLE_ENDIAN字节顺序的常量。按照此顺序,多字节值的字节顺序是从最低有效位到最高有效位的。也就是我们所说的小端模式。

4、创建只读缓冲区

asReadOnlyBuffer()方法的作用:创建共享此缓冲区内容的新的只读字节缓冲区。新缓冲区的内容将为此缓冲区的内容。此缓冲区的内容更新在新缓冲区是可见的,担心缓冲区将是只读且不允许修改共享内容。这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置、限制、标记等值将与此缓冲区相等。

public class ReadOnlydemo {
    public static void main(String[] args) {
        byte[] array1 = {1,2,3,4,5};
        ByteBuffer wrap = ByteBuffer.wrap(array1);
        ByteBuffer readOnlyBuffer = wrap.asReadOnlyBuffer();
        System.out.println(wrap.isReadOnly());
        System.out.println(readOnlyBuffer.isReadOnly());
        readOnlyBuffer.rewind();
        readOnlyBuffer.put((byte)123);
    }
}

5、压缩缓冲区

compact()方法的作用:压缩此缓冲区,将缓冲区的当前位置和限制之间的字节复制到缓冲区开始的位置,即将索引p=position()处的字节复制到索引0处,将索引p+1处的字节复制到索引1处,以此类推,知道将索引limit()-1处的字节复制到索引n=limit()-1-p处。然后将缓冲区的位置设置为n+1,并将其限制标记设置为其容量。如果已经定义了标记,则丢弃。将缓冲区的位置设置为复制的字节数,而不是0,以便调用此方法后可以紧接着调用另一个相对put方法。

压缩compact的执行过程如图:

在这里插入图片描述

6、比较缓冲区的内容

比较缓冲区的内容有两种方法:equals()和compareTo()。这两办法还是有使用细节上的区别的,先来看一下ByteBuffer的equals的源码:

public boolean equals(Object ob) {
        //判断是不是自身,是自身则返回true
        if (this == ob)
            return true;
        //判断是不是ByteBuffer类的实例,如果不是返回false
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        //判断remaining()的值是否一样,不一样则返回false
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        //判断两个缓冲区的position和limit之间的字节是否相同,只要有一个不相同则返回false
        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;
    }

所以说通过equals方法比较的是position和limit之间的内容是否完全一样,即使capacity不一样也没关系。

compareTo(ByteBuffer that)方法的作用:将此缓冲区和另外一个缓冲区进行比较。比较两个缓冲区的方法是按字典顺序比较他们剩余的的元素序列,而不考虑每个序列在其对应缓冲区中的起始位置。

 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();
    }

从compareTo(ByteBuffer that)方法的源代码中可以分析出运算的3个主要逻辑。

  1. 判断两个ByteBuffer的范围是从当前ByteBuffer对象的当前位置开始的,以两个ByteBuffer对象最小的remaining()值结束,说明判断范围是remaining()交集。
  2. 如果在开始范围和结束范围之间有一个字节不同,则返回两者的减数
  3. 如果在开始与结束的范围之间每个字节都相同,则返回两者remaining()的减数。

通过源码来看,两个缓冲区的capacity可以不一样,这个特性和equals相同。

7、复制缓冲区

ByteBuffer duplicate()方法的作用:创建共享此缓冲区内容的新的字节缓冲区。新的缓冲区的内容为此缓冲区的内容,此缓冲区的内容更改在新缓冲区的是可见的,反之亦然。在创建新的缓冲区时,容量、限制、位置、标记的值与此缓冲区一致,但是这两个缓冲区的容量、限制、位置、标记的值相互独立。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。

下面示例演示了duplicate()方法与slice()方法的区别

public class DuplicateDemo {
    public static void main(String[] args) {
        byte[] array1 = {1,2,3,4,5};
        ByteBuffer buffer1 = ByteBuffer.wrap(array1);
        buffer1.position(2);
        System.out.println("buffer1 capacity = " + buffer1.capacity() +
                " limit="+buffer1.limit()+" position="+buffer1.position());
        ByteBuffer slice = buffer1.slice();
        ByteBuffer duplicate = buffer1.duplicate();
        System.out.println("slice capacity = " + slice.capacity() +
                " limit="+slice.limit()+" position="+slice.position());
        System.out.println("duplicate capacity = " + duplicate.capacity() +
                " limit="+duplicate.limit()+" position="+duplicate.position());
        slice.position(0);
        for (int i = slice.position(); i < slice.limit(); i++) {
            System.out.println(slice.get(i)+" ");
        }
        System.out.println();

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


    }
}

8、缓冲区扩容

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

public class ExtendsSize {
    /**
     * 缓冲区扩容
     * @param buffer
     * @param size
     * @return
     */
    public static ByteBuffer extendssize(ByteBuffer buffer , int size){
        ByteBuffer allocate = ByteBuffer.allocate(buffer.capacity() + size);
        allocate.put(buffer);
        return allocate;
    }


    public static void main(String[] args) {
        byte[] array = {1,2,3,4,5,6};
        ByteBuffer wrap = ByteBuffer.wrap(array);
        ByteBuffer extendssize = extendssize(wrap, 3);

        System.out.println(Arrays.toString(extendssize.array()));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java NIO(New I/O)是一个可以替代标准 Java I/O APIJava APINIO 提供了更快速、更高效的 I/O 操作方式,也可以进行非阻塞 I/O 操作。 下面是几个 NIO 核心类和接口: - Buffer:缓冲区,用于数据的读取和写入。 - Channel:通道,用于数据的读取和写入。 - Selector:选择器,用于监控多个通道的 I/O 状态,从而实现多路复用。 下面介绍一下 NIO 的主要操作步骤: 1. 创建 Buffer 对象:创建一个 Buffer 对象,用于存储数据。 2. 创建 Channel 对象:创建一个 Channel 对象,用于读取和写入数据。 3. 打开 Channel:打开一个 Channel 对象,并连接到指定的服务器。 4. 将数据写入 Buffer:将需要写入的数据写入到 Buffer 中。 5. 切换 Buffer 模式:将 Buffer 从写模式切换到读模式。 6. 从 Buffer 中读取数据:从 Buffer 中读取数据。 7. 关闭 Channel:关闭 Channel 对象。 8. 关闭 Selector:关闭 Selector 对象。 下面是一个简单的 NIO 例子: ```java import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("localhost", 8888)); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("Hello, server!".getBytes()); buffer.flip(); socketChannel.write(buffer); buffer.clear(); int num = socketChannel.read(buffer); System.out.println("Received from server: " + new String(buffer.array(), 0, num)); socketChannel.close(); } } ``` 这个例子中,我们先创建了一个 SocketChannel 对象,并连接到指定的服务器。然后,我们创建了一个 ByteBuffer 对象,将需要写入的数据写入到 ByteBuffer 中,切换 ByteBuffer 的模式,从中读取数据并输出到控制台,最后关闭 SocketChannel 对象。 希望这份简单的介绍能够帮助你更好的了解 Java NIO API
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值