- J3 - 白起
- 技术(NIO # 通道 # Channel)
这是 IO 相关的第三篇通道,主要讲解一下通道是什么,在 Java NIO 中的体系及使用。能被称为 NIO 中的三大组件之一作用肯定是不言而喻的,所以对于通道的掌握还是很重要的,那我们往下看把!
以下内容“通道”一词就是“Channel”,只是我更喜欢用中文表示而已,仅我个人喜好,并无它意。
百度词贴:
从百度解释来看:通道主要用来传输数据的一条道路。
而在 NIO 中,通道的作用也是如此:传输数据,将“原缓冲区”与“目标缓冲区”要交换的数据进行传输。
很明显通道是作用与缓冲区的,所以读了上篇本人写得《详解,NIO中的缓冲区》那么我们得出下面这张图:
在 IDEA 中我观察 Channel 的类继承关系时,发现好复杂呀,我点进源码看它的继承接口和实现类,发现超复杂,最后还是放弃通过 IDEA 看了。
所以我去看了 JDK8 的 API 文档找出了 Channel 的相关信息,如下。
1、父接口
2、直接子接口
3、所有已知实现类
Pipe
的可写端的通道。Pipe
的可读端的通道。Selector
复用的通道。Channel 体系确实很庞大,所以我们不需要全部的去深入它们,只需要知道其中的几个就行,比如:FileChannel
、ServerSocketChannel
、SocketChannel
等。
那下面就先看看 FileChannel
也是用的最多的一个其它的下次介绍。
先看类结构图:
再看 API 图:
方式一:
FileChannel fileChannel = FileChannel.open(new File("src/").toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ);
解释:根据一个指定的文件获取一个可读写的文件通道
StandardOpenOption
枚举可以指定通道的读写权限。
public enum StandardOpenOption implements OpenOption {READ, // 读WRITE, // 写APPEND, // 在写模式下,进行追加写TRUNCATE_EXISTING, // 如果文件已经存在,并且它被打开以进行WRITE访问,那么它的长度将被截断为0。如果文件仅以READ访问方式打开,则忽略此选项。CREATE, // 如果文件不存在,请创建一个新文件。如果还设置了CREATE_NEW选项,则忽略此选项。与其他文件系统操作相比,检查文件是否存在以及创建文件(如果不存在)是原子性的。CREATE_NEW, // 创建一个新文件,如果文件已经存在则失败。与其他文件系统操作相比,检查文件是否存在以及创建文件(如果不存在)是原子性的。DELETE_ON_CLOSE, // 关闭时删除文件SPARSE, // 稀疏文件。当与CREATE_NEW选项一起使用时,此选项将提示新文件将是稀疏的。当文件系统不支持创建稀疏文件时,该选项将被忽略。SYNC, // 要求对文件内容或元数据的每次更新都以同步方式写入底层存储设备。DSYNC; // 要求对文件内容的每次更新都以同步方式写入底层存储设备。
}
案例:
@Test
public void channelTest() throws IOException {// 获得一个根据指定文件路径的读写权限文件通道FileChannel fileChannel = FileChannel.open(new File("src/").toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ);// 获得一段有指定内容的缓冲区ByteBuffer source = ByteBuffer.wrap("HelloWorld,J3-baiqi".getBytes(StandardCharsets.UTF_8));// 空的缓冲区ByteBuffer target = ByteBuffer.allocate(50);log.info("fileChannel.position():{}", fileChannel.position());// 将缓冲区中的内容写入文件通道fileChannel.write(source);// 通道大小log.info("fileChannel.position():{}", fileChannel.position());// 设置读写位置fileChannel.position(0);// 将通道中的内容写到空缓冲区ad(target);// 转换缓冲区读写模式target.flip();log.info("target:,{}", new String(target.array(), 0, target.limit()));//关闭资源fileChannel.close();
}
方式二:
FileInputStream fileInputStream = new FileInputStream("src/");
FileChannel channel = Channel();
解释:根据一个文件流获取对应的文件通道,通道的读写权限由流的输入输出决定。
输入 ==》读
输出 ==》写
案例:
@Test
public void channelTest02() throws IOException {// 获取输出流FileOutputStream outputStream = new FileOutputStream("src/");// 根据输出流获得一个 “写” 权限的通道FileChannel outChannel = Channel();// 获得一个有指定内容的缓冲区ByteBuffer source = ByteBuffer.wrap("HelloWorld,J3-baiqi".getBytes(StandardCharsets.UTF_8));// 将缓冲区内容写入到通道outChannel.write(source);// ===============================================================// 获取输入流FileInputStream fileInputStream = new FileInputStream("src/");// 根据输入流获得一个 “读” 权限的通道FileChannel inChannel = Channel();// 获得一个空内容的缓冲区ByteBuffer target = ByteBuffer.allocate(50);// 将通道中的内容读到缓冲区ad(target);// 转换缓冲区读写模式target.flip();// 读出缓冲区中的内容log.info("target:,{}", new String(target.array(), 0, target.limit()));//关闭资源outChannel.close();inChannel.close();
}
上面介绍的两个案例实现了通道的基本操作获取、读、写。如果细心的人可以发现一点非常别扭的地方就是通道的读与写的理解,write
是写操作,但被 Channel 调用后就变成了将数据写入通道有点读取数据的意思,反之亦然。
对于这种别扭的地方,我们要如何区理解呢!我说说我的理解:
通道由空变成非空就是写,缓冲区向通道写入了数据;
通道由非空变成空就是读,缓冲区从通道读取了数据。
下面介绍 FileChannel 中两个文件复制操作 API ,非常方便好用,在实际项目中也是有运用。
transferTo
:将数据复制到目标对象中。transferFrom
:将数据从目标对象中复制给自己。这两个 API 的作用一样,就是作用对象不一样,如果调用方是有数据的那就用 transferTo
,反之则用 transferFrom
。
案例:
@Test
public void copyTest() throws IOException {/*需求:将一个视频文件从F:\Channel\a.mp4复制到F:\Channel\b.mp4*/// 准备输入流(源文件)FileInputStream fileInputStream = new FileInputStream("F:\Channel\a.mp4");// 准备输出流(目标文件)FileOutputStream fileOutputStream = new FileOutputStream("F:\Channel\b.mp4");// 根据流获取通道FileChannel inputStreamChannel = Channel();FileChannel outputStreamChannel = Channel();// 指向复制方法// ansferFrom(inputStreamChannel, 0, inputStreamChannel.size());ansferTo(0, inputStreamChannel.size(), outputStreamChannel);// 关闭资源fileInputStream.close();fileOutputStream.close();
}
再来对比一下原生 BIO 复制文件操作:
@Test
public void copyTest2() throws IOException {/*需求:将一个视频文件从F:\Channel\a.mp4复制到F:\Channel\b.mp4*/// 准备输入流(源文件)FileInputStream fileInputStream = new FileInputStream("F:\Channel\a.mp4");// 准备输出流(目标文件)FileOutputStream fileOutputStream = new FileOutputStream("F:\Channel\b.mp4");//存储数据的字节数组byte[] b = new byte[1024];while (true) {//从输入流中读取数据到字节数组中int res = ad(b);//判断是否读到文件末尾,是就跳出循环if (res == -1) {break;}//将字节数组中的数据通过输出流,写到目标文件中fileOutputStream.write(b, 0, res);}fileInputStream.close();fileOutputStream.close();
}
两者对比一下,是不是 NIO 的方式较为简单。
不过通过案例对比虽然 NIO FileChannel 方式操作较为简单,但是,在效率上并不比 InputStream 或 OutputStream 高很多,这是因为 NIO 的出现最主要的就是解决阻塞问题,通过 NIO 把线程变成非阻塞这样就提高效率。
而 NIO 的非阻塞与 Socket 相关的通道有关即网络 IO,这些后面会说,在这里只是提一嘴。
通篇下来,难点基本上是没有的,主要就是理解 Channel 的作用:传输数据的通道。
然后就简单介绍了通道中用的比较多的文件通道(FileChannel)的基本使用,对于操作文件使用它还是比传统的方式简单的,至少在 API 方面是有体现,而我也做了相关案例。
那这就是 Channel 相关的内容了,虽然本片讲的简单了点,但毕竟不是专业 API 讲解,有时间还是建议看看通道中其他方法的使用。
好了,今天的内容到这里就结束了,关注我,我们下期见
查阅或参考资料:
《NIO与Socket编程指南》高洪严著
联系方式:
QQ:1491989462,做个好友,来个点赞之交。
由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。
如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。
感谢您的阅读,十分欢迎并感谢您的关注。
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CSDN:J3 - 白起
掘金:J3-白起
知乎:J3-白起
这是一个技术一般,但热衷于分享;经验尚浅,但脸皮够厚;明明年轻有颜值,但非要靠才华吃饭的程序员。
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
本文发布于:2024-02-01 15:50:43,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170677384337718.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |