服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java NIO下ByteBuffer的常用方法学习

Java NIO下ByteBuffer的常用方法学习

2023-05-17 14:05宁轩 Java教程

这篇文章主要带大家来初步学习一下NIO 中的 ByteBuffer的应用与常用方法,文中的示例代码讲解详细,对我们深入学习Java有一定的帮助,感兴趣的可以了解一下

前言

咱就说,基础不牢地动山摇,以前欠下的债迟早都要补回来,开始安排 Netty, 从 NIO 开始学起, 学习嘛, 肯定是先从案例开始学起

今日任务:

初步学习 NIO 中的 ByteBuffer

文件读取案例

一个小小的案例, 读取 test.txt 文件的内容, 四步走:

  • 获取文件流
  • 准备缓冲区
  • 写入缓冲区
  • 遍历读取

代码展示

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private static void getFileContent(){
    // 输入输出流
    try(FileChannel channel = new FileInputStream("C:\Users\My\Desktop\learn\demo\demo\test.txt").getChannel()){
        // 准备缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        // 循环遍历
        while(true){
            // 从 channel 读取数据, 向 buffer 写入
            int len = channel.read(buffer);
            if (len == -1){
                System.out.println("没有内容了");
                break;
            }
            // 打印 buffer 内容
            // 切换至 读模式
            buffer.flip();
            // 遍历查看是否还有剩余未读数据
            while(buffer.hasRemaining()){
                byte b = buffer.get();
                System.out.println(b);
            }
            // 切换为写模式
            buffer.clear();
        }
    }catch (Exception e){
        System.out.println("文件未找到");
    }
}

打印结果

test.txt文件内容如下所示

Java NIO下ByteBuffer的常用方法学习

接下来执行上述代码, 结果如下

Java NIO下ByteBuffer的常用方法学习

可以看到打印出来的结果是 ASCII 编码表的值,所以我们把输出转为 char 类型, 结果如下

Java NIO下ByteBuffer的常用方法学习

Buffer

Buffer继承自Object类,是基本类型元素的线性有限序列(容器)。除内容外,Buffer的基本属性有:Limit—限制,Capacity—容量,Position—位置。对着三个变量操作,可以完成几乎所有对Buffer的代码操作。

今天开始学习 Netty ,那就绕不过 NIO, 在 NIO 包下的 Buffer 有以下七种实现:

  • ByteBuffer
  • ShortBuffer
  • FloatBuffer
  • CharBuffer
  • DoubleBuffer
  • IntBuffer
  • LongBuffer

实际上就是八种数据类型的相关 Buffer, 但是我只听说过 ByteBuffer, 也是使用最广泛的, 至于为什么我也不知道, 希望以后有机会能过来填坑, 有大佬知道的话也可以评论区说一下

ByteBuffer常用方法

在 java 中 ByteBuffer 的常用方法有以下七种:

  • put: 写入
  • get: 读数据
  • flip: 切换读模式
  • rewind: 重新重头开始读
  • mark: 记录当前下标
  • reset: 回到 mark 位置
  • clear: 切换写模式
  • compact: 切换写模式, 同时将未读数据移动到首部

工具方法 selectAll()

为了方便观察当前 ByteBuffer 所处的位置, 容量等信息, 写了下面这个方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static void selectAll(ByteBuffer buffer){
    System.out.println();
    int limit = buffer.limit();
    System.out.print("pos = " + buffer.position() + "     " + "lim = " + buffer.limit() + "     " + "cap = " + buffer.capacity());
    System.out.println();
    for (int i = 0; i < limit; i++) {
        if (i < 10){
            System.out.print(" |  ");
        }else{
            System.out.print(" | ");
        }
        System.out.print(i);
    }
    System.out.println();
    System.out.println("--------------------------------------------------------------------------");
    for (int i = 0; i < limit; i++) {
        byte b = buffer.get(i);
        System.out.print(" |  ");
        System.out.print((char) b);
        if (b == 0){
            System.out.print(0);
        }
    }
    System.out.println();
}

打印结果如下:

Java NIO下ByteBuffer的常用方法学习

根据打印结果我们可以很直观的看到当前 ByteBuffer 每个下标上的元素是什么, 同时也能看到当前指针所处的位置(Position)

初始化

以下测试用例的 ByteBuffer 采用同一个 ByteBuffer

?
1
ByteBuffer buffer = ByteBuffer.allocate(16);

buffer.put()

put 方法就是往 ByteBuffer 中写入数据, 具体操作如下述代码所示

?
1
2
3
4
5
6
private static void bufferPut(ByteBuffer buffer){
    buffer.put(new byte[]{'a', 'b', 'c', 'd'});
    selectAll(buffer);
    buffer.put(new byte[]{'1'});
    selectAll(buffer);
}

执行结果如下:

Java NIO下ByteBuffer的常用方法学习

图中和代码一样, 是分两次打印当前 ByteBuffer 的内容, pos 就是下一次写入的下标

buffer.get()

get 方法是获取元素的, 但是我们如果在刚写入之后就去读取的话, 是什么都读取不到的, 具体如下所示

Java NIO下ByteBuffer的常用方法学习

可以看到, 我们读取出来当前的元素为 0, 我们进入源码看一下 get 方法

Java NIO下ByteBuffer的常用方法学习

如图所示, 我们通过 get 方法获取到的元素都是当前 pos 的下一个坐标元素, 在之前的 put 方法中, 我们看到最后 pos 是指向了 5 是下一个 写入下标

我们有两种方法可以获取到元素:

  • 通过下标获取
  • 将写入模式更改为读模式

通过下标获取: 直接输入下标就可以获取到了

Java NIO下ByteBuffer的常用方法学习

buffer.flip()

flip 方法: 将读模式转换为写模式

Java NIO下ByteBuffer的常用方法学习

还是刚才的例子, 使用方法 buffer.flip() 之后, 可以看到 pos 变成了 1, lim 变成了 5 , cap 不变

这里 pos 代表的是下一个 get 的下标, lim 代表的是当前的长度

注意: 在调用 flip 进入读模式之后,后续如果调用 get() 导致 position 大于等于了 limit 的值,程序会抛出 BufferUnderflowException 异常。这点从之前 get 的源码也可以看出来。

buffer.rewind()

rewind 方法可以理解为下标归零, 也可以理解为重新开始, 重头开始. 代码展示如下所示

Java NIO下ByteBuffer的常用方法学习

我们点进源代码也可以看到, 它实际上就是将 pos 赋值了 0, 将 mark 赋值为 -1

Java NIO下ByteBuffer的常用方法学习

buffer.mark() & buffer.reset()

这里将 mark 方法和 reset 方法一起讲, 他俩是相辅相成的一个关系, 二者缺一不可
mark 方法标记当前下标, reset 回到 mark 标记的下标

Java NIO下ByteBuffer的常用方法学习

可以看到, 当我们执行 mark 方法的时候记录了当前的下标, 执行 reset 方法的时候 pos 又更改为了 1

buffer.clear() & buffer.compact()

clear 方法和 compact 方法都是对当前 ByteBuffer 写入 , clear 方法是从头开始写入, compact 方法是将未读内容移至头部然后再写入

Java NIO下ByteBuffer的常用方法学习

可以看到, 在执行完 clear 方法之后, 新增的元素是从下标 0 开始写入的

Java NIO下ByteBuffer的常用方法学习

可以看到, 在执行完 compact 方法之后, 它先是将没有读取到的数据放置头部, 在接下来 put 的时候对后面的内容进行了一个覆盖

全部代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
 * @Author ningxuan
 * @Date 2022/10/31 21:28
 */
public class ByteBufferTest {
    public static void main(String[] args) {
        // 读文件案例
//        getFileContent();
        // 初始化 ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);
        // 写测试
        bufferPut(buffer);  // a , b , c , d, 1
        bufferGet(buffer);
        buffer.flip();
//        System.out.println((char) buffer.get());    // a    pos == 0
//        System.out.println((char)buffer.get());     // b    pos == 1
//        buffer.rewind();
//        System.out.println((char)buffer.get());     // c    pos == 0
//        System.out.println((char)buffer.get());     // d    pos == 1
//        bufferMarkTest(buffer);
        bufferCompact(buffer);
    }
    private static void bufferCompact(ByteBuffer buffer){
        selectAll(buffer);
        buffer.get();
        buffer.get();
        selectAll(buffer);
        buffer.compact();
        selectAll(buffer);
        buffer.put(new byte[]{'e', 'h'});
        selectAll(buffer);
    }
    private static void bufferClear(ByteBuffer buffer){
        selectAll(buffer);
        System.out.println();
        buffer.clear();
        buffer.put(new byte[]{'e', 'h'});
        selectAll(buffer);
    }
    private static void bufferMarkTest(ByteBuffer buffer){
        byte b;
        b = buffer.get();   // a
        System.out.println((char) b);
        buffer.mark();
        System.out.println("position = " + buffer.position());
        b = buffer.get();   // b
        System.out.println((char) b);
        b = buffer.get();   // c
        System.out.println((char) b);
        buffer.reset();
        System.out.println("position = " + buffer.position());
        b = buffer.get();   // b
        System.out.println((char) b);
        b = buffer.get();   // c
        System.out.println((char) b);
    }
    private static void bufferGet(ByteBuffer buffer){
        byte b = buffer.get(0);
//        System.out.println((char)b);
        b = buffer.get(1);
//        System.out.println((char)b);
    }
    private static void bufferPut(ByteBuffer buffer){
        buffer.put(new byte[]{'a', 'b', 'c', 'd'});
//        selectAll(buffer);
        buffer.put(new byte[]{'1'});
//        selectAll(buffer);
    }
    private static void selectAll(ByteBuffer buffer){
        System.out.println();
        int limit = buffer.limit();
        System.out.print("pos = " + buffer.position() + "     " + "lim = " + buffer.limit() + "     " + "cap = " + buffer.capacity());
        System.out.println();
        for (int i = 0; i < limit; i++) {
            if (i < 10){
                System.out.print(" |  ");
            }else{
                System.out.print(" | ");
            }
            System.out.print(i);
        }
        System.out.println();
        System.out.println("--------------------------------------------------------------------------");
        for (int i = 0; i < limit; i++) {
            byte b = buffer.get(i);
            System.out.print(" |  ");
            System.out.print((char) b);
            if (b == 0){
                System.out.print(0);
            }
        }
        System.out.println();
    }
    private static void getFileContent(){
        // 输入输出流
        try(FileChannel channel = new FileInputStream("C:\Users\My\Desktop\learn\demo\demo\test.txt").getChannel()){
            // 准备缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(10);
            // 循环遍历
            while(true){
                // 从 channel 读取数据, 向 buffer 写入
                int len = channel.read(buffer);
                if (len == -1){
                    System.out.println("没有内容了");
                    break;
                }
                // 打印 buffer 内容
                // 切换至 读模式
                buffer.flip();
                // 遍历查看是否还有剩余未读数据
                while(buffer.hasRemaining()){
                    byte b = buffer.get();
                    System.out.println((char) b);
                }
                // 切换为写模式
                buffer.clear();
            }
        }catch (Exception e){
            System.out.println("文件未找到");
        }
    }
}

以上就是Java NIO下ByteBuffer的常用方法学习的详细内容,更多关于Java ByteBuffer的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7163153552032923662

延伸 · 阅读

精彩推荐