Channel 中的数据都需要暂存到 Buffer 中,才可以被程序使用。
因为 Channel 没有方向性,所以 Buffer 引入了读模式和写模式来区分读写
Buffer 的作用是 存一批数据再处理,提高 IO 的效率 。没有 Buffer 就只能一个字节一个字节的处理
public static void main(String[] args) throws IOException {
// 1.读取文件
FileChannel channel = new FileInputStream("/Users/wangzhihao/workspace/study-demo/netty-demo/data.txt").getChannel();
// 2.获取 buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
while (true) {
// 3.将文件内容读取到 buffer
int read = channel.read(buffer);
if (read == -1) {
break;
}
// 4.将 buffer 切换到读模式
buffer.flip();
// 5.读取 buffer 中的内容
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.println("(char)b = " + (char)b);
}
// 6.将 buffer 切换回写模式
buffer.clear();
}
}
在 UTF-8 编码中,一个
常见字母数字占一个字节
拉丁文占两个字节
中韩日文占三个字节
其他罕见字符占四个字节
在 GBK 编码中,一个
中文字符占 2 个字节
ByteBuffer
主要实现类
HeapByteBuffer
JVM 内的堆内存,读写操作效率低,会受到 GC 影响
DirectByteBuffer
OS 内存,读写操作效率高,不会受到 GC 影响。但 不主动析构,会造成内存的泄漏
获取方式
- ByteBuffer.allocate() 一旦分配空间,大小不可以动态调整
- encode() 字符串的 encode() 方法
核心结构
ByteBuffer是一个类似数组的结构,整个结构中包含三个主要的状态
- Capacity
buffer的容量,类似于数组的size- Position
buffer当前缓存的下标,在读取操作时记录读到了那个位置,在写操作时记录写到了那个位置。从0开始,每读取一次,下标+1- Limit
读写限制,在读操作时,设置了你能读多少字节的数据,在写操作时,设置了你还能写多少字节的数据
所谓的读写模式,本质上就是这几个状态的变化。主要有Position和Limit联合决定了Buffer的读写数据区域。
核心 API
写数据
- channel.read(buffer)
- buffer.put(byte)
- buffer.put(byte[])
读数据
-
channel 的 write 方法,从 buffer 中读取数据并写入文件(buffer)
-
buffer.get() 方法,每调用一次都会改变 position 的位置
-
rewind 方法,将 position 重置为 0 ,用于重复读取数据
// 获取 buffer ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put(new byte[]{'a', 'b', 'c'}); // 将 buffer 切换到读模式 buffer.flip(); // 读取 buffer 中的内容 while (buffer.hasRemaining()) { byte b = buffer.get(); System.out.println("(char)b = " + (char) b); } System.out.println("------------------------"); // 将 position 置为 0 buffer.rewind(); // 读取 buffer 中的内容 while (buffer.hasRemaining()) { byte b = buffer.get(); System.out.println("(char)b = " + (char) b); }
-
mark & reset 方法, 通过 mark 方法标记当前 position ,通过 reset 方法跳回标记,重新从 mark 位置执行(获取数据)
-
get(i) 方法,获取特定 position 上的数据,不会影响 position
tip
StandardCharsets.UTF_8.encode("pine");
encode 和 wrap 方法转字符串为 buffer 会自动设置读模式,再次手动设置会读不到数据(看 flip() 源码)