一、Channel(通道)介绍

  通常来说NIO中的所有IO都是从 Channel(通道) 开始的。

  • 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。

  • 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。

  数据读取和写入操作图示:

  【Java】Java NIO 之 Channel 通道(三)

  Java NIO Channel通道和流非常相似,主要有以下几点区别:

  • 通道可以读也可以写,流一般来说是单向的(只能读或者写,所以之前我们用流进行IO操作的时候需要分别创建一个输入流和一个输出流)。

  • 通道可以异步读写。

  • 通道总是基于缓冲区Buffer来读写。

  Java NIO中最重要的几个Channel的实现:

  • FileChannel: 用于文件的数据读写

  • DatagramChannel: 用于UDP的数据读写

  • SocketChannel: 用于TCP的数据读写,一般是客户端实现

  • ServerSocketChannel: 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现

  类层次结构:

  下面的UML图使用Idea生成的。

  【Java】Java NIO 之 Channel 通道(三)

 

二、FileChannel的使用

2.1、基本用法

  示例一:使用FileChannel读取数据到Buffer(缓冲区)以及利用Buffer(缓冲区)写入数据到FileChannel:

  1 /**
  2  * 1、通道(Channel):
  3  *  用于源节点与目标节点的连接。在Java NIO中负责缓冲区数据的传输。
  4  *  Channel本身不存储数据,因此需要缓冲区进行传输
  5  *
  6  * 2、通道的主要实现类
  7  *  java.nio.channels.Channel 接口
  8  *      |-- FIleChannel
  9  *      |-- SocketChannel
 10  *      |-- ServerSocketChannel
 11  *      |-- DatagramChannel
 12  *
 13  * 3、获取通道
 14  *    1)Java 针对支持通道的类提供类 getChannel() 方法
 15  *      本地IO:
 16  *      FileInputStream/FileOutputStream
 17  *      RandomAccessFile
 18  *
 19  *      网络IO:
 20  *      Socket
 21  *      ServerSocket
 22  *      DatagramSocket
 23  *
 24  *    2)在JDK 1.7 中的 NIO.2 针对各个通道提供类静态方法 open()
 25  *    3)在JDK 1.7 中的 NIO.2 的Files 工具类的 newByteChannel()
 26  *
 27  * 4、通道之间的数据传输
 28  *      transferFrom()
 29  *      transferTo()
 30  *
 31  * 5、分散(Scatter)与聚集(Gather)
 32  * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 33  * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 34  *
 35  * 6、字符集:Charset
 36  * 编码:字符串 -> 字节数组
 37  * 解码:字节数组 -> 字符串
 38  */
 39 public class ChannelTest {
 40 
 41 
 42     // 1】利用通道完成文件的复制
 43     @Test
 44     public void test1(){
 45 
 46         FileInputStream fis = null;
 47         FileOutputStream fos = null;
 48 
 49         FileChannel inChannel = null;
 50         FileChannel outChannel = null;
 51         try {
 52             fis = new FileInputStream("tomcat.png");
 53             fos = new FileOutputStream("tomcat2.png");
 54 
 55             // 1、获取通道
 56             inChannel = fis.getChannel();
 57             outChannel = fos.getChannel();
 58 
 59             // 2、分配指定大小的缓存区
 60             ByteBuffer buf = ByteBuffer.allocate(1024);
 61 
 62             // 3、将通道中的数据存入缓冲区中
 63             int len;
 64             while ((len = inChannel.read(buf)) != -1) {
 65                 buf.flip(); // 切换成读取数据的模式
 66                 // 4、将缓冲区中的数据写入通道中
 67                 outChannel.write(buf);
 68                 buf.clear(); // 清空缓冲区
 69             }
 70         } catch (FileNotFoundException e) {
 71             e.printStackTrace();
 72         } catch (IOException e) {
 73             e.printStackTrace();
 74         } finally {
 75             // 5、关闭通道
 76             if(outChannel != null) {
 77                 try {
 78                     outChannel.close();
 79                 } catch (IOException e) {
 80                     e.printStackTrace();
 81                 }
 82             }
 83             if(inChannel != null) {
 84                 try {
 85                     inChannel.close();
 86                 } catch (IOException e) {
 87                     e.printStackTrace();
 88                 }
 89             }
 90             if(fos != null) {
 91                 try {
 92                     fos.close();
 93                 } catch (IOException e) {
 94                     e.printStackTrace();
 95                 }
 96             }
 97             if(fis != null) {
 98                 try {
 99                     fis.close();
100                 } catch (IOException e) {
101                     e.printStackTrace();
102                 }
103             }
104         }
105     }
106  }

  示例二:使用直接缓冲区完成文件的复制(内存映射文件)

 1     // 2】使用直接缓冲区完成文件的复制(内存映射文件)
 2     // 异常需要使用try-catch块处理
 3     @Test
 4     public void test2() throws IOException {
 5         // READ 读文件
 6         FileChannel inChannel = FileChannel.open(Paths.get("tomcat.png"), StandardOpenOption.READ);
 7         // WRIT 写文件 CREATE_NEW 文件存在就报错,不存在就创建 CREATE 覆盖
 8         FileChannel outChannel = FileChannel.open(Paths.get("tomcat2.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
 9 
10         // 内存映射文件,直接缓冲区内存在物理内存中
11         MappedByteBuffer inMapappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
12         MappedByteBuffer outMapappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
13 
14         // 直接对缓冲区进行数据的读写操作
15         byte[] dst = new byte[1024];
16         ByteBuffer buf;
17         int len = 0;
18         while (inMapappedBuf.hasRemaining()){
19             len = dst.length > inMapappedBuf.remaining() ? inMapappedBuf.remaining() : dst.length;
20 //            System.out.println(len);
21             inMapappedBuf.get(dst, 0, len) ;
22             outMapappedBuf.put(dst, 0, len);
23         }
24     }

2.2、通道之间数据传输

  在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel传输到另外一个channel。

  transferFrom()

  FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中

 1     // 3】通道之间数据传输(也是直接缓冲区方式)
 2     // 异常需要使用try-catch块处理
 3     @Test
 4     public void test3() throws IOException {
 5         // READ 读文件
 6         FileChannel inChannel = FileChannel.open(Paths.get("tomcat.png"), StandardOpenOption.READ);
 7         // WRIT 写文件 CREATE_NEW 文件存在就报错,不存在就创建 CREATE 存在覆盖,不存在创建
 8         FileChannel outChannel = FileChannel.open(Paths.get("tomcat2.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
 9 
10 //        inChannel.transferTo(0, inChannel.size(), outChannel);
11         outChannel.transferFrom(inChannel, 0, inChannel.size());
12 
13         outChannel.close();
14         inChannel.close();
15     }
View Code

相关文章: