一、Channel 简介
 Java NIO 中的 Channel 有些类似于 Stream,但又有些不同:

  • 一个 Channel 即可读又可写,而 Stream 通常只能单向操作,即流分为输入流(只能读)和输出流(只能写)
  • Channel 的读和写可以异步进行
  • Channel 总是从 Buffer 中读数据或者向 Buffer 中写数据

 如上所述,你可以将 Channel 中的数据读到一个 Buffer 中,也可以将一个 Buffer 中的数据写入到一个 Channel,如下图所示:

java 用channel实现对话 java channel stream区别_java 用channel实现对话


二、Channel 的实现

 Channel 在 Java NIO 中有以下几个最重要的实现:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

 FileChannel 负责文件数据的读写,DatagramChannel 通过 UDP 协议读写网络中传输的数据,SocketChannel 通过 TCP 协议读写网络中传输的数据,ServerSocketChannel 负责监听新的 TCP 连接,就像 Web 服务器那样,会为每一个新的连接创建一个SocketChannel。
 下面是一个使用 FileChannel 将数据读到一个 Buffer 中的简单示例:

// 创建一个随机存取文件流从磁盘读取文件
RandomAccessFile aFile = new RandomAccessFile("F:\\temp\\file.txt", "rw");
// 获取流的Channel:一个流对象只有一个Channel,同时这个Channel会通过文件的绝对路径直接连接着文件
FileChannel inChannel = aFile.getChannel();
// 创建一个Buffer
ByteBuffer buf = ByteBuffer.allocate(48);
// 将Channel连接着的本地文件的所有数据读到Buffer中
int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {
	System.out.println("Read " + bytesRead);// 读取到的文件的字节长度
	buf.flip();// 将Buffer的position值复位到0索引的位置,以从Buffer的起始处开始获取字节数据
	while (buf.hasRemaining()) {// 判断Buffer中是否还有内容
		System.out.print((char) buf.get());// 有则获取当前position位置的字节数据并打印,调用buf.get()时position会调用nextGetIndex()使position后移1位
	}
	buf.clear();// 清空Buffer
	bytesRead = inChannel.read(buf);//再次进行写操作
	System.out.println(bytesRead);// -1
}
aFile.close();// 关闭通道

Tip
  1️⃣这里并没有像传统 IO 那样,先将输入流中的文件数据读到一个字节数组中,而是将数据读到一个 Buffer 中
  2️⃣Channel 中的数据一旦写入到 Buffer 中就不再存在于 Channel 中
 RandomAccessFile.getChannel() 的源码如下:可见同一个 RandomAccessFile 的对象获取到的 Channel 对象也是同一个

public final FileChannel getChannel() {
    synchronized (this) {
        if (channel == null) {
            channel = FileChannelImpl.open(fd, path, true, rw, this);
        }
        return channel;
    }
}

 流在关闭时会自动关闭其 Channel,因此可以不手动关闭Channel(当然也可以手动关闭 Channel),RandomAccessFile.close()方法的源码如下:

public void close() throws IOException {
    synchronized (closeLock) {
        if (closed) {
            return;
        }
        closed = true;
    }
    if (channel != null) {
        channel.close();//自动关闭channel
    }

    fd.closeAll(new Closeable() {
        public void close() throws IOException {
           close0();
       }
    });
}