JavaSE-11-ByteBuffer(NIO核心组件)
ByteBuffer 是 Java NIO 中非常重要的组件,用于处理字节数据。它是 Buffer 的子类,专门用于处理字节数据。本文详细解释 ByteBuffer 的各个方法和用法。
ByteBuffer的出现,将IO操作的通道channel和存储读写分离,解耦,而该类也相当于将普通的byte[]数组,包装成一个有完整属性和行为操作的数据容器模型!这种组件解耦分离封装的设计模式,值得学习!
一、UML类图
二、ByteBuffer 基本概念
ByteBuffer 有四个重要属性:
-
- capacity:缓冲区容量(固定不变)
-
- position:当前读写位置(0 <= position <= limit)
-
- limit:当前读写的限制(limit <= capacity)
-
- mark:标记位置(用于重置 position)
三、ByteBuffer 创建方法
1. allocate() - 分配堆内存缓冲区
// 创建容量为1024的堆内存缓冲区 ByteBuffer buffer = ByteBuffer.allocate( 1024 ); System.out.println( "capacity: " + buffer.capacity()); // 1024 System.out.println( "position: " + buffer.position()); // 0 System.out.println( "limit: " + buffer.limit()); // 1024
2. allocateDirect() - 分配直接内存缓冲区
// 创建容量为1024的直接内存缓冲区(更高效,但创建成本高) ByteBuffer directBuffer = ByteBuffer.allocateDirect( 1024 );
3. wrap() - 包装现有字节数组
// 包装现有字节数组 byte [] data = "Hello" .getBytes(); ByteBuffer buffer = ByteBuffer.wrap(data); System.out.println( "capacity: " + buffer.capacity()); // 5 System.out.println( "position: " + buffer.position()); // 0 System.out.println( "limit: " + buffer.limit()); // 5
四、ByteBuffer 核心方法详解
1. put() - 写入数据
ByteBuffer buffer = ByteBuffer.allocate( 10 ); // put(byte b) - 写入单个字节 buffer.put(( byte ) 'H' ); System.out.println( "position: " + buffer.position()); // 1 // put(byte[] src) - 写入字节数组 buffer.put( "ello" .getBytes()); System.out.println( "position: " + buffer.position()); // 5 // put(byte[] src, int offset, int length) - 写入字节数组的一部分 byte [] data = "World" .getBytes(); buffer.put(data, 0 , 3 ); // 只写入"Wor" System.out.println( "position: " + buffer.position()); // 8 // put(int index, byte b) - 在指定位置写入(不改变position) buffer.put( 0 , ( byte ) 'h' ); // 将第一个位置的'H'改为'h' System.out.println( "position: " + buffer.position()); // 8 (不变)
2. get() - 读取数据
ByteBuffer buffer = ByteBuffer.wrap( "Hello" .getBytes()); // get() - 读取当前位置的字节 byte b = buffer.get(); // 读取'H' System.out.println(( char ) b); // H System.out.println( "position: " + buffer.position()); // 1 // get(byte[] dst) - 读取多个字节到数组 byte [] data = new byte [ 4 ]; buffer.get(data); // 读取"ello" System.out.println( new String (data)); // ello System.out.println( "position: " + buffer.position()); // 5 // get(byte[] dst, int offset, int length) - 读取字节到数组的指定位置 buffer.rewind(); // 重置position到0 byte [] target = new byte [ 10 ]; buffer.get(target, 2 , 3 ); // 从target[2]开始写入3个字节 System.out.println( new String (target, 2 , 3 )); // Hel // get(int index) - 读取指定位置的字节(不改变position) buffer.rewind(); byte first = buffer.get( 0 ); // 读取第一个字节 System.out.println(( char ) first); // H System.out.println( "position: " + buffer.position()); // 0 (不变)
3. flip() - 翻转缓冲区(准备读取)
ByteBuffer buffer = ByteBuffer.allocate( 10 ); // 写入数据 buffer.put( "Hello" .getBytes()); System.out.println( "写入后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 写入后 - position: 5, limit: 10 // flip() - 翻转为读取模式 buffer.flip(); System.out.println( "翻转后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 翻转后 - position: 0, limit: 5 // 现在可以读取数据了 byte [] data = new byte [buffer.remaining()]; buffer.get(data); System.out.println( new String (data)); // Hello
4. rewind() - 重绕缓冲区
ByteBuffer buffer = ByteBuffer.wrap( "Hello" .getBytes()); // 读取所有数据 byte [] data = new byte [buffer.remaining()]; buffer.get(data); System.out.println( new String (data)); // Hello System.out.println( "position: " + buffer.position()); // 5 // rewind() - 重置position为0,limit不变 buffer.rewind(); System.out.println( "重绕后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 重绕后 - position: 0, limit: 5 // 可以重新读取数据 byte [] data2 = new byte [buffer.remaining()]; buffer.get(data2); System.out.println( new String (data2)); // Hello
5. clear() - 清空缓冲区
ByteBuffer buffer = ByteBuffer.allocate( 10 ); buffer.put( "Hello" .getBytes()); System.out.println( "写入后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 写入后 - position: 5, limit: 10 // clear() - 清空缓冲区(实际只是重置position和limit) buffer.clear(); System.out.println( "清空后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 清空后 - position: 0, limit: 10 // 缓冲区中的数据实际上还在,只是被标记为可覆盖 buffer.put( "World" .getBytes()); // 现在缓冲区中是"World",但原来的数据可能还在内存中
6. compact() - 压缩缓冲区
ByteBuffer buffer = ByteBuffer.allocate( 10 ); buffer.put( "HelloWorld" .getBytes()); buffer.flip(); // 准备读取 // 读取部分数据 byte [] first5 = new byte [ 5 ]; buffer.get(first5); System.out.println( new String (first5)); // Hello System.out.println( "读取后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 读取后 - position: 5, limit: 10 // compact() - 压缩缓冲区,将未读数据移到开头 buffer.compact(); System.out.println( "压缩后 - position: " + buffer.position() + ", limit: " + buffer.limit()); // 输出: 压缩后 - position: 5, limit: 10 // 缓冲区中现在是"World"在开头,后面是空位
7. remaining() 和 hasRemaining() - 检查剩余数据
ByteBuffer buffer = ByteBuffer.wrap( "Hello" .getBytes()); System.out.println( "remaining: " + buffer.remaining()); // 5 System.out.println( "hasRemaining: " + buffer.hasRemaining()); // true buffer.get( new byte [ 3 ]); // 读取3个字节 System.out.println( "remaining: " + buffer.remaining()); // 2 System.out.println( "hasRemaining: " + buffer.hasRemaining()); // true buffer.get( new byte [ 2 ]); // 读取剩余2个字节 System.out.println( "remaining: " + buffer.remaining()); // 0 System.out.println( "hasRemaining: " + buffer.hasRemaining()); // false
五、ByteBuffer 在聊天室中的应用
1. 服务端读取客户端消息
/** * 处理客户端消息 - 你的代码中的应用 */ private void handleRead (SelectionKey key) throws IOException { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate( 1024 ); // 读取数据到缓冲区 int bytesRead = clientChannel.read(buffer); if (bytesRead > 0 ) { // flip() - 翻转缓冲区,准备读取 buffer.flip(); // remaining() - 检查有多少数据可读 byte [] data = new byte [buffer.remaining()]; // get() - 读取所有剩余数据 buffer.get(data); String message = new String (data).trim(); System.out.println( "收到消息: " + message); // 处理消息... } }
2. 服务端发送消息给客户端
/** * 广播消息给所有客户端 - 你的代码中的应用 */ private void broadcastMessage (String username, String message) throws IOException { String formattedMessage = "[" + username + "]: " + message; System.out.println(formattedMessage); // wrap() - 包装字符串为ByteBuffer ByteBuffer buffer = ByteBuffer.wrap(formattedMessage.getBytes()); for (SocketChannel channel : clientMap.keySet()) { // rewind() - 重置position,因为要多次发送给不同客户端 buffer.rewind(); channel.write(buffer); } }
3. 客户端发送消息
/** * 发送消息到服务端 - NIOChatClient中的应用 */ private void sendMessage (String message) { try { // wrap() - 包装消息为ByteBuffer ByteBuffer buffer = ByteBuffer.wrap((message + "\n" ).getBytes()); // write() - 发送缓冲区中的数据 socketChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } }
六、ByteBuffer 状态管理示例
public class ByteBufferStateExample { public void demonstrateBufferStates () { ByteBuffer buffer = ByteBuffer.allocate( 10 ); System.out.println( "初始状态: position=" + buffer.position() + ", limit=" + buffer.limit() + ", capacity=" + buffer.capacity()); // 1. 写入模式 buffer.put( "Hello" .getBytes()); System.out.println( "写入后: position=" + buffer.position() + ", limit=" + buffer.limit()); // 2. 准备读取 - flip() buffer.flip(); System.out.println( "翻转后: position=" + buffer.position() + ", limit=" + buffer.limit()); // 3. 读取部分数据 byte [] partial = new byte [ 3 ]; buffer.get(partial); System.out.println( "部分读取后: position=" + buffer.position() + ", limit=" + buffer.limit()); // 4. 压缩缓冲区 - compact() buffer.compact(); System.out.println( "压缩后: position=" + buffer.position() + ", limit=" + buffer.limit()); // 5. 清空缓冲区 - clear() buffer.clear(); System.out.println( "清空后: position=" + buffer.position() + ", limit=" + buffer.limit()); } }
七、ByteBuffer 类型转换方法
public class ByteBufferTypeConversion { public void demonstrateTypeMethods () { ByteBuffer buffer = ByteBuffer.allocate( 20 ); // 写入各种类型数据 buffer.putInt( 123456 ); // 4字节 buffer.putLong( 987654321L ); // 8字节 buffer.putDouble( 3.14159 ); // 8字节 buffer.putChar( 'A' ); // 2字节 // 准备读取 buffer.flip(); // 按相同顺序读取 int intValue = buffer.getInt(); long longValue = buffer.getLong(); double doubleValue = buffer.getDouble(); char charValue = buffer.getChar(); System.out.println( "int: " + intValue); // 123456 System.out.println( "long: " + longValue); // 987654321 System.out.println( "double: " + doubleValue); // 3.14159 System.out.println( "char: " + charValue); // A } }
ByteBuffer 是 NIO 的核心组件,正确理解和使用它的各种方法对于编写高效的 NIO 程序至关重要。聊天室应用中,ByteBuffer 负责处理所有网络数据的读写操作,是实现高性能通信的基础。