目录:
有几种 Channel 和 Buffer ,以下是 NIO 中主要 Channel 实现类的列表,这些通道包括 UDP + TCP 网络 IO 和文件 IO:
- FileChannel :文件通道
 - DatagramChannel :数据报通道
 - SocketChannel :套接字通道
 - ServerSocketChannel :服务器套接字通道
 
这些类也有一些有趣的接口,但为了简单起见,这里暂时不提,后续会进行学习的。
以下是 NIO 中的核心 Buffer 实现,其实就是 7 种基本类型:
- ByteBuffer
 - CharBuffer
 - ShortBuffer
 - IntBuffer
 - LongBuffer
 - FloatBuffer
 - DoubleBuffer
 
NIO 还有一个 MappedByteBuffer,它与内存映射文件一起使用,同样这个后续再讲。
Selectors 选择器
选择器允许单个线程处理多个通道。 如果程序打开了许多连接(通道),但每个连接只有较低的流量,使用选择器就很方便。 例如,在聊天服务器中, 以下是使用 Selector 处理 3 个 Channel 的线程图示:
要使用选择器,需要使用它注册通道。 然后你调用它的 select() 方法。 此方法将阻塞,直到有一个已注册通道的事件准备就绪。 一旦该方法返回,该线程就可以处理这些事件。 事件可以是传入连接,接收数据等。
Channel (通道)
NIO 通道类似于流,但有一些区别:
- 通道可以读取和写入。 流通常是单向的(读或写)。
 - 通道可以异步读取和写入。
 - 通道始终读取或写入缓冲区,即它只面向缓冲区。
 
如上所述,NIO 中总是将数据从通道读取到缓冲区,或将数据从缓冲区写入通道。 这是一个例子:
// 文件内容是 123456789 RandomAccessFile accessFile = new RandomAccessFile("D:\\test\\1.txt", "rw"); FileChannel fileChannel = accessFile.getChannel();  ByteBuffer buffer = ByteBuffer.allocate(48);  int data = fileChannel.read(buffer); // 将 Channel 的数据读入缓冲区,返回读入到缓冲区的字节数Buffer(缓冲区)
使用 Buffer 与 Channel 交互,数据从通道读入缓冲区,或从缓冲区写入通道。
缓冲区本质上是一个可以写入数据的内存块,之后可以读取数据。 Buffer 对象包装了此内存块,提供了一组方法,可以更轻松地使用内存块。
Buffer 的基本用法
使用 Buffer 读取和写入数据通常遵循以下四个步骤:
- 将数据写入缓冲区
 - 调用 buffer.flip() 反转读写模式
 - 从缓冲区读取数据
 - 调用 buffer.clear() 或 buffer.compact() 清除缓冲区内容
 
将数据写入Buffer 时,Buffer 会跟踪写入的数据量。 当需要读取数据时,就使用 flip() 方法将缓冲区从写入模式切换到读取模式。 在读取模式下,缓冲区允许读取写入缓冲区的所有数据。
读完所有数据之后,就需要清除缓冲区,以便再次写入。 可以通过两种方式执行此操作:通过调用 clear() 或调用 compact() 。区别在于 clear() 是方法清除整个缓冲区,而 compact() 方法仅清除已读取的数据,未读数据都会移动到缓冲区的开头,新数据将在未读数据之后写入缓冲区。
这是一个简单的缓冲区用法示例:
public class ChannelExample {     public static void main(String[] args) throws IOException {     // 文件内容是 123456789         RandomAccessFile accessFile = new RandomAccessFile("D:\\test\\1.txt", "rw");         FileChannel fileChannel = accessFile.getChannel();          ByteBuffer buffer = ByteBuffer.allocate(48); //创建容量为48字节的缓冲区          int data = fileChannel.read(buffer); // 将 Channel 的数据读入缓冲区,返回读入到缓冲区的字节数         while (data != -1) {             System.out.println("Read " + data); // Read 9             buffer.flip(); // 将 buffer 从写入模式切换为读取模式             while (buffer.hasRemaining()) {                 System.out.print((char) buffer.get()); // 每次读取1byte,循环输出 123456789             }             buffer.clear(); // 清除当前缓冲区             data = fileChannel.read(buffer); // 将 Channel 的数据读入缓冲区         }         accessFile.close();     } }Buffer 的 capacity,position 和 limit
缓冲区有 3 个需要熟悉的属性,以便了解缓冲区的工作原理。 这些是:
