提交 7121cecd 编写于 作者: 沉默王二's avatar 沉默王二 💬

nio

上级 6d182ea3
......@@ -219,12 +219,12 @@
## Java NIO
- [Java 中的 NIO 比传统 IO 强在哪里?](docs/nio/why.md)
- [一文彻底解释清楚Java 中的BIO、NIO和AIO?](docs/nio/BIONIOAIO.md)
- [详解Java NIO的Buffer缓冲区和Channel管道](docs/nio/rumen.md)
- [聊聊 Java 中的Paths、Files](docs/nio/paths-files.md)
- [Java NIO 比传统 IO 强在哪里?](docs/nio/nio-better-io.md)
- [一文彻底解释清楚Java 中的NIO、BIO和AIO](docs/nio/BIONIOAIO.md)
- [详解Java NIO的Buffer缓冲区和Channel通道](docs/nio/buffer-channel.md)
- [聊聊 Java NIO中的Paths、Files](docs/nio/paths-files.md)
- [使用Java NIO完成网络编程](docs/nio/network-connect.md)
- [一文彻底理解Java IO模型(阻塞IO非阻塞IO/IO多路复用)](docs/nio/moxing.md)
- [使用Java NIO完成网络通信](docs/nio/network-connect.md)
## 重要知识点
......
......@@ -255,11 +255,12 @@ export const sidebarConfig = sidebar({
collapsible: true,
prefix: "nio/",
children: [
"why",
"rumen",
"nio-better-io",
"BIONIOAIO",
"buffer-channel",
"paths-files",
"moxing",
"network-connect",
"BIONIOAIO",
],
},
{
......
......@@ -227,9 +227,9 @@ head:
### Java NIO
- [Java 中的 NIO 比传统 IO 强在哪里?](nio/why.md)
- [一文彻底解释清楚Java 中的BIO、NIO和AIO?](nio/BIONIOAIO.md)
- [详解Java NIO的Buffer缓冲区和Channel管道](nio/rumen.md)
- [Java NIO 比传统 IO 强在哪里?](nio/nio-better-io.md)
- [一文彻底解释清楚Java 中的NIO、BIO和AIO](nio/BIONIOAIO.md)
- [详解Java NIO的Buffer缓冲区和Channel通道](nio/buffer-channel.md)
- [聊聊 Java 中的Paths、Files](nio/paths-files.md)
- [一文彻底理解Java IO模型(阻塞IO非阻塞IO/IO多路复用)](nio/moxing.md)
- [使用Java NIO完成网络通信](nio/network-connect.md)
......
---
title: 这才是你最想要的 C++ 学习路线
shortTitle: 这才是你最想要的 C++ 学习路线
description: 大家好,我是 Rocky0429。 对于零基础想要学学 C++ 的同学,我希望你们要先明白一件事: C++ 是一门极难掌握的编程语言,内容多且杂且难懂。所以 如果你想要想要学好 C++,你要花很多的时间和精力。当然这件事我也…
tags:
- 优质文章
category:
- 知乎
head:
- - meta
- name: keywords
content: 编程语言,C++ 编程,C++
---
球友好。
不知道你现在大几,我就按照大学阶段从 0 到 1 的方式来给你整一份学习路线。早上问了一些专业的 C++ 选手,他们也给我推荐了一些路线,我这里全部整理出来。
对于零基础想要学学 C++ 的同学,我希望你们要先明白一件事:
> C++ 是一门极难掌握的编程语言,内容多且杂且难懂。
所以**如果你想要想要学好 C++,你要花很多的时间和精力。**
当然这件事我也想告诉你:如果你在刚开始学或者学了很短的一段时间,发现自己学不会,默默告诉自己“这不是我的原因,是 C++ 的原因,都赖它太难”。
我不希望同学们还没入门就放弃。
C++ 较底层,语法非常的灵活,这就造成了它的语法规则比较的繁杂,这里面包含了 C 语言的内容,相比较于 Python、Java 等同等的高级语言,学习 C++ 的时间成本更大。
C++ 是和 C 语言完全不同的编程语言,但是在基础语句和语法上,C++ 和 C 很像,我认为 C++ 可以看成是 C 语言的延展:C ++ 就是一个加了面向对象特性的 C 语言。
如果你有 C 语言基础的话,学习 C++ 会快一些,有余力可以先学 C 语言的同学,可以看我下面这篇文章:
[](https://zhuanlan.zhihu.com/p/416406696)
当然没有 C 语言,直接学 C++ 也是没问题的。
**C++的上限非常高,但是分阶段性逐步学习是没有问题的,一步步的学,慢慢领悟,总有一天会熟练掌握的。**
C++ 语言的学习其实就**三个阶段**就好了:
**(1) 入门阶段**
这个阶段的学习主要是熟悉 C++ 语言的语法知识。
在这个阶段要做到理解对象的思想方法,培养自己的编程思维能力。
目标是可以开发一些像贪吃蛇这种简单的控制台小程序。
**(2) 进阶阶段**
进阶阶段的学习主要是要掌握 C++ 标准模板库(STL)、设计模式、数据结构基础以及 UI 界面开发、数据库开发等高级技能。
在这个阶段是要达到可以开发复杂的程序,达到工作中 C++ 开发程序员的能力。
**(3) 应用阶段**
这个是实战阶段,要具备一定的综合性应用软件开发能力。
这个阶段就是多观摩别人的项目,看人家的写法,模仿项目,学习其中的思想,一点点的积累,一步步形成自己的东西,厚积而薄发,慢慢你就会发现你也可以了。
**注意!下面都是超极干的干货,记得先帮我** **[@Rocky0429](https://www.zhihu.com/people/62ca0e445f981d4af47929fcd6b318d5) 点个赞呀,么么哒。**
## **一、入门阶段**
**入门阶段的学习主要是熟悉 C++ 语言的语法知识。**
除了基础的变量、常量、关键字、数据类型、运算符、数组、函数、指针、结构体外,还要学习 C++ 的面向对象编程思想、命名空间 namespace、引用、函数扩展、类的封装、构造和析构、继承、多态、异常处理等内容。
**语言部分的学习建议不要拖太久,一定要规划好时间,一鼓作气,不然自己容易泄气!**
### 1.视频推荐
此时同学们应该是毫无基础或者稍微有点 C 语言基础的小白。
**对于小白来说,不建议上来就看书,因为干看看不懂,容易劝退。**
**可以先从视频教程开始,教材为辅。**
我当初 C++ 视频是在 b 站看的黑马程序员的 C++ 课程(我不是他们的托儿
从 0 到 1 教 C++,三百多个小节,每个小节时间都不是很长,除了个别几个在二十多分钟,其余的基本上都在几分钟到十几分钟之间。
![](https://pic1.zhimg.com/v2-af56691fabfaf868656fe9e843b27ea0_b.jpg)
每一个阶段都会有相应的小项目教学,对初学者来说是很友好的。
[](https://link.zhihu.com/?target=https%3A//www.bilibili.com/video/av41559729%3Fp%3D1)
**看视频的时候不是看看就过去了,编程毕竟是门一门手艺活,孰能生巧。**
**建议一边看,一边将视频中的示例或者小项目教学自己也实现一下,刚开始不会可以照着敲,比只看不动手强一百倍。**
### 2.书籍推荐
入门阶段的书籍为辅,怎么为辅呢?
就是**视频看完一个阶段,然后就可以去看书上对应阶段的内容,这样看书,一方面看书的时候会很快,容易理解,另一方面可以印证自己在看视频的时候一些不太理解的地方。**
入门阶段推荐两本书,一本薄的,一本厚的,都是超级经典的书籍。
**《Essential C++》**
《Essential C++》是一本内容不多但很实用的 C++ 入门书籍,这本书强调的是快速上手与理解 C++ 编程。
主要围绕一系列逐渐复杂的程序问题,以及用以解决这些问题的语言特性展开讲解。
你不只学到 C++ 的函数和结构,也会学习到它们的设计目的和基本原理。
![](https://pic1.zhimg.com/v2-e0a9cb1393f60a3aed27d48fb1a537d0_b.jpg)
**《C++ Primer》**
很多人 C++ 入门的时候会推荐《C++ Primer Plus》,但是我更强烈推荐的是这本《C++ Primer》!
![](https://pic1.zhimg.com/v2-524949a233b4983b65808cad5f52c4c0_b.jpg)
这两本书我都看过,很有发言权,当时我学习的时候《C++ Primer》还是第 4 版,现在都到第 5 版了!
《C++ Primer》堪称 C++ 语法学习的最权威书籍,非常全面地讲解了C++的语法以及C++11的各种新特性,看完之后真的帮助特别大!
如果有时间建议至少看两遍以上!时面向 C++ 语言的初学者,是一本很友好的自学教材!而且例程和习题丰富,相信认真读过之后,可以完成 C++ 语言入门这个目标!!
![](https://pic2.zhimg.com/v2-f695d1e147877b4b6504b7baab4ca7e1_b.jpg)
如果你在这个阶段觉得差不多了,可以尝试找一些在线的练习题做下,如果你不知道去哪找,那可以去下面这个初学者练习编程巩固语法的绝佳去处。
它有专门的 C++ 入门编程练习题,专门练习语法和大家的编程逻辑,从变量、数据类型这些**基础语法**,到数组、字符串这种**复合类型**,再到**函数、面向对象**,以及在 C++ 中很重要的 **STL**,最后再来点**综合练习**,差不多 **70 多道题**,够你练的。
> 传送门:[在线编程\_编程学习|练习题\_C++|系统设计题库](https://link.zhihu.com/?target=https%3A//www.nowcoder.com/link/pc_kol_wenqlgd)
![](https://pic3.zhimg.com/v2-571bc740cdf5bb8478016070a666c1f2_b.jpg)
除了编程练习以外,如果你想知道你自己的知识点掌握的如何,也可以做一下专项练习,一共 1700+ 的题目。
![](https://pic2.zhimg.com/v2-3f2e5f071ea592eca87b1d03430ae7f9_b.jpg)
以类似试卷的形式,可以很好的检验自己的学习成果,不管是对之后应对考试,或者应付笔试面试都很有帮助。
> 传送门:[在线编程\_编程学习|练习题\_C++|系统设计题库](https://link.zhihu.com/?target=https%3A//www.nowcoder.com/link/pc_kol_wenqlgd)
![](https://pic3.zhimg.com/v2-6df9513d87fc2a488a97c9b88f4a02aa_b.jpg)
## **二、进阶阶段**
在进阶阶段,你已经对 C++ 有一定的认知了。
这个时候我们可以深入学习 C++ 标准模板库(STL)、设计模式、数据结构基础以及 UI 界面开发、数据库开发等高级技能。
### 1.书籍推荐
**《C++标准程序库》**
关于 STL,可以先读这本侯捷老师翻译的《C++ 标准程序库》。
通过这本书对STL有个基本认识,学会使用 STL。
![](https://pic4.zhimg.com/v2-785063863072d1a7c331c56abbf1fc9b_b.jpg)
**《STL源码剖析》**
读完 《C++ 标准程序库》,就可以来读这本侯捷老师编写的《STL源码剖析》了。
这本书建议必读!
这本书讲解了 C++ 底层实现,主要包括 C++ 底层内存管理、各种容器的数据结构实现、常见算法的实现等。
可以帮助深入理解C++底层,同时也是对数据结构的复习和巩固。
![](https://pic2.zhimg.com/v2-d83bc2eb69c0c6dd6a763d2767cfec79_b.jpg)
**《Effective C++》**
《Effective C++》讲了 C++ 编程的 55 条准则,提高你的 C++ 编程质量,也是侯捷老师翻译的!
这本书有助于梳理在编写 C++ 程序时的一些常见错误和注意事项,也是面试常考的。
![](https://pic1.zhimg.com/v2-98fe5a44943bf3519617ab7903c6f480_b.jpg)
**《深度探索C++对象模型》**
《深度探索C++对象模型》这本书讲解了C++面向对象特性的底层实现机制。
侯捷老师翻译的,看完这本书,对C++面向对象的理解帮助极大,建议必读!
![](https://pic2.zhimg.com/v2-c79565e30dd5f84d4dc0000290cae339_b.jpg)
### 2.视频推荐
不知道大家注意了没,上面我推荐了四本书,都和一个人有关:侯捷老师。
书要么是他翻译的,要么是他写的,C++ 领域 YYDS!同意吧?
侯捷老师当然也有讲课,针对书都有对应内容的视频课程!
怎么样?香吧!原先这些课本来可以在 B 站看的,但是不知道为啥,都下架了,sad。
**我搜集了一套非常全的侯捷老师的 C++ 视频,有需要的可以去下载保存备用。**
![](https://pic3.zhimg.com/v2-479158e9f7ddaf0e2b04a53d78fc1f62_b.jpg)
**视频地址:**
[](https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzI0NjAxMDU5NA%3D%3D%26mid%3D2475922860%26idx%3D1%26sn%3D2115db904d80e58532bf4533c60c31b8%26chksm%3Dff22f221c8557b370dca60c4e209108be82156dbc957181d6de711e7cf1b4dbafa2bf753c0b5%26token%3D1153516574%26lang%3Dzh_CN%23rd)
## **三、应用阶段**
其实编程语言就是要多练,怎么多练,就是代码量。
自己多写,然后多观摩别人的项目,看人家的写法,模仿项目,学习其中的思想,一点点的积累,一步步形成自己的东西,厚积而薄发,慢慢你就会发现你也可以了。
我在这里推荐几个非常优秀的 GitHub 上的实战项目。
**一共推荐 14 个开源项目,7 个入门级,7 个企业级**,保证都是值得学习的开源项目!
### **入门级**
**1.TinyWebServer(3.6k+ star)**
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.
* 使用 **线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和模拟Proactor均实现)** 的并发模型
* 使用**状态机**解析HTTP请求报文,支持解析**GET和POST**请求
* 访问服务器数据库实现web端用户**注册、登录**功能,可以请求服务器**图片和视频文件**
* 实现**同步/异步日志系统**,记录服务器运行状态
* 经Webbench压力测试可以实现**上万的并发连接**数据交换
> 传送门:[GitHub - qinguoyi/TinyWebServer: Linux下C++轻量级Web服务器](https://link.zhihu.com/?target=https%3A//github.com/qinguoyi/TinyWebServer)
**2.libqalculate(545 star)**
使用 C++ 编写的多功能计算器桌面应用、库和 CLI 程序。
它易于使用功能强大,支持大型可定制函数库、单位计算和转换、符号计算(包括积分和方程)。
作为用户你可以直接在命令行中使用,作为开发者你也可以在自己的项目中使用这个库。官方还制作了[Qt](https://link.zhihu.com/?target=https%3A//github.com/Qalculate/qalculate-qt)[GTK](https://link.zhihu.com/?target=https%3A//github.com/Qalculate/qalculate-gtk)两个版本的 GUI 计算器应用。
![](https://pic3.zhimg.com/v2-227ef62815eb9b0023b5d65d69640ebe_b.jpg)
**3.spdlog(12.7k+)**
快速、上手简单的 C++ 日志库。示例代码:
```text
#include "spdlog/spdlog.h"
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
```
**4.CPlusPlusThings(15.4k+star)**
这是一个适合 C++ 初学者从入门到进阶的教程。
解决了面试者想要深入 C++ 及如何入坑 C++ 的问题。
除此之外,该仓库拓展了更加深入的语法分析、多线程并发等的知识,是一个比较全面的 C++ 从入门学习到进阶提升的项目。
**5.indicators(1.7k+star)**
一个使用 C++ 编写的进度条库,你可以用它在命令行中实现美观的进度条。它使用方便、线程安全、支持多种进度条样式。
**6.sudoku(299 star)**
C++ 实现的命令行数独游戏。600 余行代码,初学者也可以轻松学习。
![](https://pic4.zhimg.com/v2-94f3e6666f15a1a75a0d0f1bcb626c7b_b.jpg)
**7.dbg-macro(1.7k+)**
打日志是 C++ 开发中必不可少的一种 debug 方式,[dbg-macro](https://link.zhihu.com/?target=https%3A//github.com/sharkdp/dbg-macro)[rust-lang](https://link.zhihu.com/?target=https%3A//www.rust-lang.org/) 中 的 [dbg](https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/macro.dbg.html) 启发,提供比 printf 和 std::cout 更好的宏函数。主要有如下特点:
* 美观的彩色输出(当输出不是交互式终端时,颜色将自动禁用)
* 兼容 C++11,并且是 header-only
* 支持基础类型和 STL 容器类型的输出
* 除了基本信息外,还输出变量名和类型
* 启用 DBG*MACRO*DISABLE 生成 release 版
```text
#include <vector>
#include <dbg.h>
// You can use "dbg(..)" in expressions:
int factorial(int n) {
if (dbg(n <= 1)) {
return dbg(1);
} else {
return dbg(n * factorial(n - 1));
}
}
int main() {
std::string message = "hello";
dbg(message); // [example.cpp:15 (main)] message = "hello" (std::string)
const int a = 2;
const int b = dbg(3 * a) + 1; // [example.cpp:18 (main)] 3 * a = 6 (int)
std::vector<int> numbers{b, 13, 42};
dbg(numbers); // [example.cpp:21 (main)] numbers = {7, 13, 42} (size: 3) (std::vector<int>)
dbg("this line is executed"); // [example.cpp:23 (main)] this line is executed
factorial(4);
return 0;
}
```
![](https://pic2.zhimg.com/v2-aa9deff96924a663b94f464ef1d5c729_b.jpg)
### **企业级**
**1.rocksdb(21.1k+ star)**
该项目是由 Fackbook 数据库团队基于 levelDB 开发,用 C++ 编写的高性能键值存储引擎。
键值均支持二进制流,能够充分利用多核 CPU 获得高性能,并兼容 levelDB 的 API 可谓是青出于蓝而胜于蓝。RocksDB 当下十分流行,一些开源数据库底层存储用的就是它。
> 传送门:[https://github.com/facebook/rocksdb](https://link.zhihu.com/?target=https%3A//github.com/facebook/rocksdb)
**2.winmerge(2.2k+ star)**
一个用 C++ 编写的 Windows 比较和合并工具。
它可以比较文件和文件夹,以直观的可视化格式来显示两者甚至三者之间的差异,操作简单。
![](https://pic3.zhimg.com/v2-2ddb343635048094c9085f2a35b9623e_b.jpg)
> 传送门:[GitHub - WinMerge/winmerge: WinMerge is an Open Source differencing and merging tool for Windows. WinMerge can compare both folders and files, presenting differences in a visual text format that is easy to understand and handle.](https://link.zhihu.com/?target=https%3A//github.com/WinMerge/winmerge)
**3.workflow(6.1k+ star)**
搜狗开源的 C++ 服务器引擎。
支撑搜狗几乎所有后端 C++ 在线服务:
* 所有搜索服务
* 云输入法
* 广告
* ....
每日处理超百亿请求。
这是一个设计轻盈优雅的企业级程序引擎,可以满足大多数 C++ 后端开发需求。
> 传送门:[https://github.com/sogou/workflow](https://link.zhihu.com/?target=https%3A//github.com/sogou/workflow)
**4.srpc(825 star)**
搜狗基于 C++ Workflow 的高性能 RPC 框架。
与 thrift/brpc 协议兼容,支持 protobuf/thrift IDL一键迁移,核心代码量仅 1w 行。
![](https://pic1.zhimg.com/v2-60dc921e0bf06be7d5fbd01453416774_b.jpg)
> 传送门:[GitHub - sogou/srpc: RPC based on C++ Workflow. Supports Baidu bRPC, Tencent tRPC, thrift protocols.](https://link.zhihu.com/?target=https%3A//github.com/sogou/srpc)
**5.nvui(1.2k+ star)**
跨平台现代的 Neovim 前端。
它使用起来流畅,动画效果细腻、美观,你可以用 Neovim 和 nvui 自由定制出一个高颜值、最懂你的编辑器。
![](https://pic1.zhimg.com/v2-d18b08069ee057b74957f803e8258620_b.jpg)
> 传送门:[https://github.com/rohit-px2/nvui](https://link.zhihu.com/?target=https%3A//github.com/rohit-px2/nvui)
**6.filament(12.1k+ star)**
filament 是轻量级实时物理渲染引擎。
能够用来做游戏渲染引擎或者音视频编辑工程,当你需要处理 3D 渲染效果,又不想引入庞大的游戏引擎时,可以考虑使用它尤其是 Android 平台。
![](https://pic3.zhimg.com/v2-555adb9b154e014672f2764544ee9caa_b.jpg)
> 传送门:[GitHub - google/filament: Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WebGL2](https://link.zhihu.com/?target=https%3A//github.com/google/filament)
**7.hplayer(451 star)**
一个基于 C++ 实现的多画面播放器。
能播放文件源、网络源、设备捕获源,界面为多画面监控网格,可自由切换多画面风格,支持拖拽与合并。适合想要入门音视频、播放器开发的小伙伴们学习,该项目用到的技术栈:
* Qt 实现界面
* FFmpeg 获取帧、编解码、转码
* OpenCV 处理图片
* OpenGL 渲染视频帧
![](https://pic4.zhimg.com/v2-d6c32f79d0090fc7f2c2b1c1416ede3b_b.jpg)
> 传送门:[https://github.com/ithewei/hpla](https://link.zhihu.com/?target=https%3A//github.com/ithewei/hplayer)
## 希望对你有所帮助
其实说实话,本科的同学 C++ 水平真是天差地別的。
你能学到什么程度,就看你的努力程度了。
最后再送给学习 C++ 的同学一段浙大翁凯老师讲课说的话:
> 学 C++ (计算机)一定要有一个非常强大的心理状态。
>
> 为什么呢?
>
> C++ (计算机)的所有东西都是人做出来的,别人能想出来的我也一定想得出来,在 C++(计算机)里头没有任何黑魔法,所有的东西只不过是我现在不知道而已。
>
> 总有一天我会把它里面的细节搞明白的!
大家加油!
>参考链接:[https://zhuanlan.zhihu.com/p/435927070](https://zhuanlan.zhihu.com/p/435927070),整理:沉默王二
此差异已折叠。
---
title: 详解Java NIO的Buffer缓冲区和Channel管道
title: 详解Java NIO的Buffer缓冲区和Channel通道 - 沉默王二 - java进阶之路
shortTitle: Buffer和Channel
category:
- Java核心
tag:
- Java NIO
description: Java程序员进阶之路,小白的零基础Java教程,Java NIO 快速入门(buffer缓冲区、Channel管道、Selector选择器)
description: Java NIO 中的 Buffer 和 Channel 是 NIO 系统的核心组件。Buffer 负责存储数据,提供了对数据的读写操作。Channel 代表了与 I/O 设备(如文件或套接字)之间的连接。它提供了从源设备到 Buffer 的数据读取能力和从 Buffer 到目标设备的数据写入能力。
author: 沉默王二
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio,buffer,channel,selector,java nio,java buffer,java channel
content: java,Channel,Buffer,nio
---
# 12.3 BufferChannel
# 12.3 BufferChannel
首先我们来看看**IO 和 NIO 的区别**
首先我们再来回顾一下 [IO 和 NIO 的区别](https://tobebetterjavaer.com/nio/nio-better-io.html)
- 可简单认为:**IO 是面向流的处理,NIO 是面向块(缓冲区)的处理**
- 面向流的 I/O 系统**一次一个字节地处理数据**
- 一个面向块(缓冲区)的 I/O 系统**以块的形式处理数据**
NIO 主要有**个核心部分组成**
NIO 主要有**个核心部分组成**
- **buffer 缓冲区**
- **Channel 管道**
- **Selector 选择器**
- **Buffer 缓冲区**
- **Channel 通道**
在 NIO 中,并不是以流的方式来处理数据的,而是以 buffer 缓冲区和 Channel **配合使用**来处理数据的。
在 NIO 中,并不是以流的方式来处理数据的,而是以 buffer 缓冲区和 Channel **配合使用**来处理数据的。
简单理解一下:
可以把 Channel 道比作铁路,buffer 缓冲区比作成火车(运载着货物)
可以把 Channel 道比作铁路,buffer 缓冲区比作成火车(运载着货物)
而我们的 NIO 就是**通过 Channel 道运输着存储数据的 Buffer 缓冲区的来实现数据的处理**
而我们的 NIO 就是**通过 Channel 道运输着存储数据的 Buffer 缓冲区的来实现数据的处理**
要时刻记住:Channel 不与数据打交道,它只负责运输数据。与数据打交道的是 Buffer 缓冲区
- **Channel-->运输**
- **Buffer-->数据**
相对于传统 IO 而言,**流是单向的**。对于 NIO 而言,有了 Channel 道这个概念,我们的**读写都是双向**的(铁路上的火车能从广州去北京、自然就能从北京返还到广州)!
相对于传统 IO 而言,**流是单向的**。对于 NIO 而言,有了 Channel 道这个概念,我们的**读写都是双向**的(铁路上的火车能从广州去北京、自然就能从北京返还到广州)!
### buffer 缓冲区核心要点
### Buffer 缓冲区
我们来看看 Buffer 缓冲区有什么值得我们注意的地方。
......@@ -49,7 +49,7 @@ Buffer 是缓冲区的抽象类:
![](https://cdn.tobebetterjavaer.com/stutymore/rumen-20230404151539.png)
其中 ByteBuffer 是**用得最多的实现类**(在道中读写字节数据)。
其中 ByteBuffer 是**用得最多的实现类**(在道中读写字节数据)。
![](https://cdn.tobebetterjavaer.com/stutymore/rumen-20230404152253.png)
......@@ -64,8 +64,6 @@ Buffer 类维护了 4 个核心变量来提供**关于其所包含的数组信
- 位置 Position **下一个要被读或写的元素的位置**。Position 会自动由相应的 `get()``put()`函数更新。
- 标记 Mark 一个备忘位置。**用于记录上一次读写的位置**
### buffer 代码演示
首先展示一下**是如何创建缓冲区的,核心变量的值是怎么变化的**
```java
......@@ -181,9 +179,23 @@ System.out.println(new String(bytes, 0, bytes.length));
![](https://cdn.tobebetterjavaer.com/stutymore/rumen-20230404160412.png)
### FileChannel 通道核心要点
### Channel 通道
Channel 通道**只负责传输数据、不直接操作数据**。操作数据都是通过 Buffer 缓冲区来进行操作!通常,通道可以分为两大类:文件通道和套接字通道。
FileChannel:用于文件 I/O 的通道,支持文件的读、写和追加操作。FileChannel 允许在文件的任意位置进行数据传输,支持文件锁定以及内存映射文件等高级功能。FileChannel 无法设置为非阻塞模式,因此它只适用于阻塞式文件操作。
SocketChannel:用于 TCP 套接字 I/O 的通道。SocketChannel 支持非阻塞模式,可以与 Selector(下文会讲)一起使用,实现高效的网络通信。SocketChannel 允许连接到远程主机,进行数据传输。
与之匹配的有ServerSocketChannel:用于监听 TCP 套接字连接的通道。与 SocketChannel 类似,ServerSocketChannel 也支持非阻塞模式,并可以与 Selector 一起使用。ServerSocketChannel 负责监听新的连接请求,接收到连接请求后,可以创建一个新的 SocketChannel 以处理数据传输。
DatagramChannel:用于 UDP 套接字 I/O 的通道。DatagramChannel 支持非阻塞模式,可以发送和接收数据报包,适用于无连接的、不可靠的网络通信。
这篇我们主要来讲 FileChannel,SocketChannel、ServerSocketChannel 和 DatagramChannel 会放到[后面的章节中](https://tobebetterjavaer.com/nio/network-connect.html)讲解。
#### 文件通道 FileChannel
Channel 通道**只负责传输数据、不直接操作数据**。操作数据都是通过 Buffer 缓冲区来进行操作!
可以通过下面的方式打开一个通道。
```java
FileChannel.open(Paths.get("docs/配套教程.md"), StandardOpenOption.WRITE);
......@@ -191,7 +203,7 @@ FileChannel.open(Paths.get("docs/配套教程.md"), StandardOpenOption.WRITE);
这里我们用到了 [Paths](https://tobebetterjavaer.com/nio/paths-files.html),这个后面也会讲到。
使用**FileChannel 配合缓冲区**实现文件复制的功能:
①、使用**FileChannel 配合 ByteBuffer 缓冲区**实现文件复制的功能:
```java
try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
......@@ -207,9 +219,11 @@ try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itw
}
```
我们创建一个容量为 1024 的 ByteBuffer 作为缓冲区。在循环中,我们从源文件的 FileChannel 读取数据到缓冲区。当 read() 方法返回 -1 时,表示已经到达文件末尾。读取数据后,我们调用 `flip()` 方法,以便在缓冲区中准备好要写入的数据。然后,我们将缓冲区的内容写入目标文件的 FileChannel。在写入完成后,我们调用 `clear()` 方法重置缓冲区,以便在下一次迭代中重用它
我们创建一个容量为 1024 的 ByteBuffer 作为缓冲区。在循环中,我们从源文件的 FileChannel 读取数据到缓冲区。当 `read()` 方法返回 -1 时,表示已经到达文件末尾
使用**内存映射文件**的方式实现**文件复制**的功能(直接操作缓冲区):
读取数据后,我们调用 `flip()` 方法,以便在缓冲区中准备好要写入的数据。然后,我们将缓冲区的内容写入目标文件的 FileChannel(`write()` 方法)。在写入完成后,我们调用 `clear()` 方法重置缓冲区,以便在下一次迭代中重用它。
②、使用**内存映射文件(MappedByteBuffer)**的方式实现**文件复制**的功能(直接操作缓冲区):
```java
try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
......@@ -226,9 +240,19 @@ try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itw
}
```
源文件的 MappedByteBuffer 设置为只读模式(READ_ONLY),而目标文件的 MappedByteBuffer 设置为读写模式(READ_WRITE)。在循环中,我们逐字节地从源文件的 MappedByteBuffer 读取数据并将其写入目标文件的 MappedByteBuffer。这样就实现了文件复制功能。利用内存映射文件实现的文件复制,可能会比使用 ByteBuffer 的方法更快。
MappedByteBuffer 是 Java NIO 中的一个类,它继承自 `java.nio.ByteBuffer`。MappedByteBuffer 用于表示一个内存映射文件,即将文件的一部分或全部映射到内存中,以便通过直接操作内存来实现对文件的读写。这种方式可以提高文件 I/O 的性能,因为操作系统可以直接在内存和磁盘之间传输数据,无需通过 Java 应用程序进行额外的数据拷贝。
通常与 FileChannel 一起使用,可以通过调用 FileChannel 的 `map()` 方法创建 MappedByteBuffer 对象。`map()` 方法接受三个参数:映射模式(FileChannel.MapMode)映射起始位置和映射的长度。
映射模式包括只读模式(READ_ONLY)、读写模式(READ_WRITE)和专用模式(PRIVATE)。
我们设置源文件的 MappedByteBuffer 为只读模式(READ_ONLY),目标文件的 MappedByteBuffer 为读写模式(READ_WRITE)。
通道之间通过`transfer()`实现数据的传输(直接操作缓冲区):
在循环中,我们逐字节地从源文件的 MappedByteBuffer 读取数据并将其写入目标文件的 MappedByteBuffer。这样就实现了文件复制功能。利用内存映射文件(MappedByteBuffer)实现的文件复制,可能会比使用 ByteBuffer 的方法更快。
需要注意的是,使用 MappedByteBuffer 进行文件操作时,数据的修改可能不会立即写入磁盘。可以通过调用 MappedByteBuffer 的 `force()` 方法将数据立即写回磁盘。
③、通道之间通过`transfer()`实现数据的传输(直接操作缓冲区):
```java
try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
......@@ -239,7 +263,49 @@ try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itw
}
```
### 直接与非直接缓冲区
FileChannel 的 `transferTo()` 方法是一个高效的文件传输方法,它允许将文件的一部分或全部内容直接从源文件通道传输到目标通道(通常是另一个文件通道或网络通道)。这种传输方式可以避免将文件数据在用户空间和内核空间之间进行多次拷贝,提高了文件传输的性能。
`transferTo()` 方法接受以下三个参数:
- position:源文件中开始传输的位置。
- count:要传输的字节数。
- target:接收数据的目标通道。
需要注意的是,`transferTo()` 方法可能无法一次传输所有请求的字节。在实际应用中,你可能需要使用循环来确保所有字节都被传输。
```java
public class FileChannelTransferToLoopExampleWithPaths {
public static void main(String[] args) {
Path sourcePath = Paths.get("logs/itwanger/paicoding.txt");
Path destinationPath = Paths.get("logs/itwanger/paicoding_copy.txt");
// 使用 try-with-resources 语句确保通道资源被正确关闭
try (FileChannel sourceChannel = FileChannel.open(sourcePath, StandardOpenOption.READ);
FileChannel destinationChannel = FileChannel.open(destinationPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
long position = 0;
long count = sourceChannel.size();
// 循环传输,直到所有字节都被传输
while (position < count) {
long transferred = sourceChannel.transferTo(position, count - position, destinationChannel);
position += transferred;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
此外,`transferTo()` 方法在底层使用了操作系统提供的零拷贝功能(如 Linux 的 `sendfile()` 系统调用),可以大幅提高文件传输性能。但是,不同操作系统和 JVM 实现可能会影响零拷贝的可用性和性能,因此实际性能可能因环境而异。
零拷贝(Zero-Copy)是一种优化数据传输性能的技术,它最大限度地减少了在数据传输过程中的 CPU 和内存开销。在传统的数据传输过程中,数据通常需要在用户空间和内核空间之间进行多次拷贝,这会导致额外的 CPU 和内存开销。零拷贝技术通过避免这些多余的拷贝操作,实现了更高效的数据传输。
在 Java 中,零拷贝技术主要应用于文件和网络 I/O。FileChannel 类的 `transferTo()``transferFrom()` 方法就利用了零拷贝技术,可以在文件和网络通道之间高效地传输数据。详细参考:[深入剖析Linux IO原理和几种零拷贝机制的实现](https://zhuanlan.zhihu.com/p/83398714)
#### 直接与非直接缓冲区
直接缓冲区和非直接缓冲区的差别主要在于它们在内存中的存储方式。这里给出了直接缓冲区和非直接缓冲区的简要概述和区别:
......@@ -256,102 +322,17 @@ try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itw
- 不受垃圾回收的管理
- 在读写操作时,直接在本地内存中进行,避免了数据复制,提高了性能
- 创建: `ByteBuffer.allocateDirect(int capacity)`
- 还有前面提到的 `FileChannel.map()` 方法,会返回一个类型为 MappedByteBuffer 的直接缓冲区。
除此之外,就是前面提到的 `FileChannel.map()` 方法。
### scatter 和 gather
ByteBuffer.allocate和ByteBuffer.allocateDirect直接的差异。
Scatter 和 Gather 是 Java NIO 中两种高效的 I/O 操作,用于将数据分散到多个缓冲区或从多个缓冲区中收集数据。
![](https://cdn.tobebetterjavaer.com/stutymore/buffer-channel-20230406183808.png)
Scatter(分散):它将从 Channel 读取的数据分散(写入)到多个缓冲区。这种操作可以在读取数据时将其分散到不同的缓冲区,有助于处理结构化数据。例如,我们可以将消息头、消息体和消息尾分别写入不同的缓冲区
直接缓冲区和非直接缓冲区之间的差异
Gather(聚集):与 Scatter 相反,它将多个缓冲区中的数据聚集(读取)并写入到一个 Channel。这种操作允许我们在发送数据时从多个缓冲区中聚集数据。例如,我们可以将消息头、消息体和消息尾从不同的缓冲区中聚集到一起并写入到同一个 Channel。
来写一个完整的 demo,先看 Server。
```java
// 创建一个ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
// 接受连接
SocketChannel socketChannel = serverSocketChannel.accept();
// Scatter:分散读取数据到多个缓冲区
ByteBuffer headerBuffer = ByteBuffer.allocate(128);
ByteBuffer bodyBuffer = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {headerBuffer, bodyBuffer};
long bytesRead = socketChannel.read(buffers);
// 输出缓冲区数据
headerBuffer.flip();
while (headerBuffer.hasRemaining()) {
System.out.print((char) headerBuffer.get());
}
System.out.println();
bodyBuffer.flip();
while (bodyBuffer.hasRemaining()) {
System.out.print((char) bodyBuffer.get());
}
// Gather:聚集数据从多个缓冲区写入到Channel
ByteBuffer headerResponse = ByteBuffer.wrap("Header Response".getBytes());
ByteBuffer bodyResponse = ByteBuffer.wrap("Body Response".getBytes());
ByteBuffer[] responseBuffers = {headerResponse, bodyResponse};
long bytesWritten = socketChannel.write(responseBuffers);
// 关闭连接
socketChannel.close();
serverSocketChannel.close();
```
再来看 Client:
```java
// 创建一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 9000));
// 发送数据到服务器
String header = "Header Content";
String body = "Body Content";
ByteBuffer headerBuffer = ByteBuffer.wrap(header.getBytes());
ByteBuffer bodyBuffer = ByteBuffer.wrap(body.getBytes());
ByteBuffer[] buffers = {headerBuffer, bodyBuffer};
socketChannel.write(buffers);
// 从服务器接收数据
ByteBuffer headerResponseBuffer = ByteBuffer.allocate(128);
ByteBuffer bodyResponseBuffer = ByteBuffer.allocate(1024);
ByteBuffer[] responseBuffers = {headerResponseBuffer, bodyResponseBuffer};
long bytesRead = socketChannel.read(responseBuffers);
// 输出接收到的数据
headerResponseBuffer.flip();
while (headerResponseBuffer.hasRemaining()) {
System.out.print((char) headerResponseBuffer.get());
}
bodyResponseBuffer.flip();
while (bodyResponseBuffer.hasRemaining()) {
System.out.print((char) bodyResponseBuffer.get());
}
// 关闭连接
socketChannel.close();
```
![](https://cdn.tobebetterjavaer.com/stutymore/buffer-channel-20230406182921.png)
在这个示例中,我们使用了 Scattering 从 SocketChannel 分散读取数据到多个缓冲区,并使用 Gathering 将数据从多个缓冲区聚集写入到 SocketChannel。通过这种方式,我们可以方便地处理多个缓冲区中的数据
非直接缓冲区存储在JVM内部,数据需要从应用程序(Java)复制到非直接缓冲区,再复制到内核缓冲区,最后发送到设备(磁盘/网络)。而对于直接缓冲区,数据可以直接从应用程序(Java)复制到内核缓冲区,无需经过JVM的非直接缓冲区
### 小结
......
......@@ -12,6 +12,56 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio,多路复用,阻塞IO
---
### 01、IO的五种模型
>如果觉得枯燥可以跳过看 03、04、05 的内容。
网上的资料很多都以 IO 的五种模型([后面会再细讲](https://tobebetterjavaer.com/nio/moxing.html))为基础来讲解 NIO,而 IO 这五种模型其中又涉及到了很多概念:`同步/异步/阻塞/非阻塞/多路复用`**而不同的人又有不同的理解方式**
I/O 的五种模型如下:
- 阻塞 I/O(Blocking I/O):在这种模型中,I/O 操作是阻塞的,即执行 I/O 操作时,线程会被阻塞,直到操作完成。在阻塞 I/O 模型中,每个连接都需要一个线程来处理。因此,对于大量并发连接的场景,阻塞 I/O 模型的性能较差。
- 非阻塞 I/O(Non-blocking I/O):在这种模型中,I/O 操作不会阻塞线程。当数据尚未准备好时,I/O 调用会立即返回。线程可以继续执行其他任务,然后在适当的时候再次尝试执行 I/O 操作。非阻塞 I/O 模型允许单个线程同时处理多个连接,但可能需要在应用程序级别进行复杂的调度和管理。
- I/O 多路复用(I/O Multiplexing):这种模型使用操作系统提供的多路复用功能(如 select、poll、epoll 等),使得单个线程可以同时处理多个 I/O 事件。当某个连接上的数据准备好时,操作系统会通知应用程序。这样,应用程序可以在一个线程中处理多个并发连接,而不需要为每个连接创建一个线程。
- 信号驱动 I/O(Signal-driven I/O):在这种模型中,应用程序可以向操作系统注册一个信号处理函数,当某个 I/O 事件发生时,操作系统会发送一个信号通知应用程序。应用程序在收到信号后处理相应的 I/O 事件。这种模型与非阻塞 I/O 类似,也需要在应用程序级别进行事件管理和调度。
- 异步 I/O(Asynchronous I/O):异步 I/O 模型与同步 I/O 模型的主要区别在于,异步 I/O 操作会在后台运行,当操作完成时,操作系统会通知应用程序。应用程序不需要等待 I/O 操作的完成,可以继续执行其他任务。这种模型适用于处理大量并发连接,且可以简化应用程序的设计和开发。
下面解释一下同步/异步概念。
- 同步:在执行 I/O 操作时,应用程序需要等待操作的完成。同步操作会导致线程阻塞,直到操作完成。同步 I/O 包括阻塞 I/O、非阻塞 I/O 和 I/O 多路复用。
- 异步:在执行 I/O 操作时,应用程序不需要等待操作的完成。异步操作允许应用程序在 I/O 操作进行时继续执行其他任务。异步 I/O 模型包括信号驱动 I/O 和异步 I/O。
再来看阻塞/非阻塞的概念。
- 阻塞:在阻塞 I/O 操作中,线程必须等待 I/O 操作完成才能继续执行。在此期间,线程无法执行其他任务。
- 非阻塞:在非阻塞 I/O 操作中,线程无需等待 I/O 操作完成,即使数据尚未准备好。线程可以在等待数据时执行其他任务。
然后是多路复用的概念。
多路复用:多路复用是一种处理多个 I/O 事件的技术。它允许一个线程同时监视多个文件描述符或通道。当某个 I/O 事件准备好时,多路复用机制会通知应用程序,从而实现在单个线程中处理多个并发连接。
假设你现在是个大厨(炖个老母鸡汤,切点土豆丝/姜丝/葱丝):
- 同步/阻塞:你站在锅边,一直等到汤炖好,期间不能做其他事情,直到汤炖好才去处理其他任务。
- 同步/非阻塞:你不断地查看锅里的汤,看是否炖好。在检查的间隙,你可以处理其他任务,如切菜。但你需要不断地切换任务,确保汤炖好了就可以处理。
- 异步/信号驱动:你给锅安装一个传感器,当汤炖好时,传感器会发出信号提醒你。在此期间,你可以处理其他任务,而不用担心错过汤炖好的时机。
- 异步 I/O:你请了一个助手,让他负责炖汤。当汤炖好时,助手会通知你。你可以专心处理其他任务,而无需关心炖汤的过程。
这下清楚了吧?
### 02、select/epoll/poll/pselect/fd
还有涉及到 Unix 的`select/epoll/poll/pselect/fd`这些关键字,没有相关基础的人看起来简直是天书,这里补充一下。
- select 是 Unix 系统中最早的 I/O 多路复用技术。它允许一个线程同时监视多个文件描述符(如[套接字](https://tobebetterjavaer.com/socket/socket.html)),并等待某个文件描述符上的 I/O 事件(如可读、可写或异常)。select 的主要问题是性能受限,特别是在处理大量文件描述符时。这是因为它使用一个位掩码来表示文件描述符集,每次调用都需要传递这个掩码,并在内核和用户空间之间进行复制。
- poll 是对 select 的改进。它使用一个文件描述符数组而不是位掩码来表示文件描述符集。这样可以避免 select 中的性能问题。然而,poll 仍然需要遍历整个文件描述符数组,以检查每个文件描述符的状态。因此,在处理大量文件描述符时,性能仍然受限。
- epoll 是 Linux 中的一种高性能 I/O 多路复用技术。它通过在内核中维护一个事件表来避免遍历文件描述符数组的性能问题。当某个文件描述符上的 I/O 事件发生时,内核会将该事件添加到事件表中。应用程序可以使用 epoll_wait 函数来获取已准备好的 I/O 事件,而无需遍历整个文件描述符集。这种方法大大提高了在大量并发连接下的性能。
- pselect 是 select 的一个变体,它支持更精确的超时控制和原子性信号处理。pselect 允许应用程序在等待 I/O 事件时阻塞信号,从而避免竞争条件。然而,与 select 相比,pselect 在性能方面没有太大改进。
- fd:这个关键字指的是 "file descriptor"(文件描述符),它是 Unix 系统中用于表示打开的文件、套接字和其他 I/O 对象的整数标识符。文件描述符在 I/O 多路复用中起着关键作用,因为它们允许应用程序同时监视多个 I/O 对象。
**文件的IO就告一段落了**,我们来学习网络中的IO~~~为了更好地理解NIO,**我们先来学习一下IO的模型**~
......
......@@ -12,19 +12,112 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio,网络通信
---
# 12.5 Paths 和 Files
## NIO基础继续讲解
回到我们最开始的图:
SocketChannel 和 ServerSocketChannel 是 NIO 在网络套接字应用中两个重要的类,[之前也曾体验过](https://tobebetterjavaer.com/nio/nio-better-io.html),这里重点讲一下。
SocketChannel 是一个可连接到 TCP 网络套接字的通道,用于非阻塞模式下的客户端网络通信。在非阻塞模式下,SocketChannel 在建立连接、读取和写入数据时都不会阻塞当前线程。SocketChannel 可以和 Selector 一起使用,让我们可以用单个线程处理多个客户端连接。
Scatter 和 Gather 是 Java NIO 中两种高效的 I/O 操作,用于将数据分散到多个缓冲区或从多个缓冲区中收集数据。
Scatter(分散):它将从 Channel 读取的数据分散(写入)到多个缓冲区。这种操作可以在读取数据时将其分散到不同的缓冲区,有助于处理结构化数据。例如,我们可以将消息头、消息体和消息尾分别写入不同的缓冲区。
Gather(聚集):与 Scatter 相反,它将多个缓冲区中的数据聚集(读取)并写入到一个 Channel。这种操作允许我们在发送数据时从多个缓冲区中聚集数据。例如,我们可以将消息头、消息体和消息尾从不同的缓冲区中聚集到一起并写入到同一个 Channel。
来写一个完整的 demo,先看 Server。
```java
// 创建一个ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
// 接受连接
SocketChannel socketChannel = serverSocketChannel.accept();
// Scatter:分散读取数据到多个缓冲区
ByteBuffer headerBuffer = ByteBuffer.allocate(128);
ByteBuffer bodyBuffer = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {headerBuffer, bodyBuffer};
long bytesRead = socketChannel.read(buffers);
// 输出缓冲区数据
headerBuffer.flip();
while (headerBuffer.hasRemaining()) {
System.out.print((char) headerBuffer.get());
}
System.out.println();
bodyBuffer.flip();
while (bodyBuffer.hasRemaining()) {
System.out.print((char) bodyBuffer.get());
}
// Gather:聚集数据从多个缓冲区写入到Channel
ByteBuffer headerResponse = ByteBuffer.wrap("Header Response".getBytes());
ByteBuffer bodyResponse = ByteBuffer.wrap("Body Response".getBytes());
ByteBuffer[] responseBuffers = {headerResponse, bodyResponse};
long bytesWritten = socketChannel.write(responseBuffers);
// 关闭连接
socketChannel.close();
serverSocketChannel.close();
```
再来看 Client:
```java
// 创建一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 9000));
// 发送数据到服务器
String header = "Header Content";
String body = "Body Content";
ByteBuffer headerBuffer = ByteBuffer.wrap(header.getBytes());
ByteBuffer bodyBuffer = ByteBuffer.wrap(body.getBytes());
ByteBuffer[] buffers = {headerBuffer, bodyBuffer};
socketChannel.write(buffers);
// 从服务器接收数据
ByteBuffer headerResponseBuffer = ByteBuffer.allocate(128);
ByteBuffer bodyResponseBuffer = ByteBuffer.allocate(1024);
ByteBuffer[] responseBuffers = {headerResponseBuffer, bodyResponseBuffer};
long bytesRead = socketChannel.read(responseBuffers);
// 输出接收到的数据
headerResponseBuffer.flip();
while (headerResponseBuffer.hasRemaining()) {
System.out.print((char) headerResponseBuffer.get());
}
bodyResponseBuffer.flip();
while (bodyResponseBuffer.hasRemaining()) {
System.out.print((char) bodyResponseBuffer.get());
}
// 关闭连接
socketChannel.close();
```
![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nio/network-connect-bb1bd676-8aeb-4428-9498-230a05ee717d.jpg)
在这个示例中,我们使用了 Scattering 从 SocketChannel 分散读取数据到多个缓冲区,并使用 Gathering 将数据从多个缓冲区聚集写入到 SocketChannel。通过这种方式,我们可以方便地处理多个缓冲区中的数据。
前面我们讲了 Java NIO 的文件 IO [FileChannel](https://tobebetterjavaer.com/nio/buffer-channel.html),这篇我们来讲 Java NIO 的网络 IO:SocketChannel 和ServerSocketChannel。
NIO被叫为 `no-blocking io`,其实是在**网络这个层次中理解的**,对于**FileChannel来说一样是阻塞**
以及异步的 AsynchronousSocketChannel 和AsynchronousServerSocketChannel
我们前面也仅仅讲解了FileChannel,对于我们网络通信是还有几个Channel的~
分别对应阻塞 IO 中对应的 Socket 和 ServerSocket
......
---
title: Java 中的 NIO 比传统 IO 强在哪里?
title: Java NIO 比传统 IO 强在哪里? - 沉默王二 - java进阶之路
shortTitle: NIO和IO的区别
category:
- Java核心
tag:
- Java NIO
description: Java程序员进阶之路,小白的零基础Java教程,为什么我们要使用 Java NIO?
description: 本篇内容主要讲述了 NIO 和传统 IO 之间的差异。首先,传统 IO 采用阻塞模型,而 NIO 使用非阻塞模型,通过选择器监控多个通道上的 I/O 事件,从而提升性能与可伸缩性。其次,在文件操作方面,传统 IO 依赖字节流或字符流进行文件读写,而 NIO 则利用通道和缓冲区进行操作,性能优势相对较小。最后,在网络传输方面,传统 IO 使用 Socket 和 ServerSocket,而 NIO 提供了 SocketChannel 和 ServerSocketChannel,支持非阻塞网络传输,进一步增强并发处理能力。
author: 沉默王二
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio,java nio
content: java,nio,io
---
# 12.1 NIO和IO的区别
我花了几天时间去了解**NIO 的核心知识**,期间看了《Java 编程思想》和《疯狂 Java 讲义》中的 NIO 模块。**但是**,看完之后还是很**迷**,不知道 NIO 是干嘛用的,网上的资料和书上的知识点没有很好地对应上。
### 01、IO的五种模型
我这里先给大家展示一副传统 IO 和 NIO 的对比图,感受一下。
网上的资料很多都以 IO 的五种模型为基础来讲解 NIO,而 IO 这五种模型其中又涉及到了很多概念:`同步/异步/阻塞/非阻塞/多路复用`**而不同的人又有不同的理解方式**
![](https://cdn.tobebetterjavaer.com/stutymore/nio-better-io-20230406180538.png)
I/O 的五种模型如下:
[传统 IO](https://tobebetterjavaer.com/io/shangtou.html) 基于字节流或字符流(如 FileInputStream、BufferedReader 等)进行文件读写,以及使用 [Socket 和 ServerSocket](https://tobebetterjavaer.com/socket/socket.html) 进行网络传输。
- 阻塞 I/O(Blocking I/O):在这种模型中,I/O 操作是阻塞的,即执行 I/O 操作时,线程会被阻塞,直到操作完成。在阻塞 I/O 模型中,每个连接都需要一个线程来处理。因此,对于大量并发连接的场景,阻塞 I/O 模型的性能较差。
- 非阻塞 I/O(Non-blocking I/O):在这种模型中,I/O 操作不会阻塞线程。当数据尚未准备好时,I/O 调用会立即返回。线程可以继续执行其他任务,然后在适当的时候再次尝试执行 I/O 操作。非阻塞 I/O 模型允许单个线程同时处理多个连接,但可能需要在应用程序级别进行复杂的调度和管理。
- I/O 多路复用(I/O Multiplexing):这种模型使用操作系统提供的多路复用功能(如 select、poll、epoll 等),使得单个线程可以同时处理多个 I/O 事件。当某个连接上的数据准备好时,操作系统会通知应用程序。这样,应用程序可以在一个线程中处理多个并发连接,而不需要为每个连接创建一个线程。
- 信号驱动 I/O(Signal-driven I/O):在这种模型中,应用程序可以向操作系统注册一个信号处理函数,当某个 I/O 事件发生时,操作系统会发送一个信号通知应用程序。应用程序在收到信号后处理相应的 I/O 事件。这种模型与非阻塞 I/O 类似,也需要在应用程序级别进行事件管理和调度。
- 异步 I/O(Asynchronous I/O):异步 I/O 模型与同步 I/O 模型的主要区别在于,异步 I/O 操作会在后台运行,当操作完成时,操作系统会通知应用程序。应用程序不需要等待 I/O 操作的完成,可以继续执行其他任务。这种模型适用于处理大量并发连接,且可以简化应用程序的设计和开发。
NIO 使用[通道(Channel)和缓冲区(Buffer)](https://tobebetterjavaer.com/nio/buffer-channel.html)进行文件操作,以及使用 SocketChannel 和 ServerSocketChannel 进行网络传输。
下面解释一下同步/异步概念
传统 IO 采用阻塞式模型,对于每个连接,都需要创建一个独立的线程来处理读写操作。当一个线程在等待 I/O 操作时,无法执行其他任务。这会导致大量线程的创建和销毁,以及上下文切换,降低了系统性能
- 同步:在执行 I/O 操作时,应用程序需要等待操作的完成。同步操作会导致线程阻塞,直到操作完成。同步 I/O 包括阻塞 I/O、非阻塞 I/O 和 I/O 多路复用。
- 异步:在执行 I/O 操作时,应用程序不需要等待操作的完成。异步操作允许应用程序在 I/O 操作进行时继续执行其他任务。异步 I/O 模型包括信号驱动 I/O 和异步 I/O。
NIO 使用非阻塞模型,允许线程在等待 I/O 时执行其他任务。这种模式通过使用选择器(Selector)来监控多个通道(Channel)上的 I/O 事件,实现了更高的性能和可伸缩性。
再来看阻塞/非阻塞的概念。
- 阻塞:在阻塞 I/O 操作中,线程必须等待 I/O 操作完成才能继续执行。在此期间,线程无法执行其他任务。
- 非阻塞:在非阻塞 I/O 操作中,线程无需等待 I/O 操作完成,即使数据尚未准备好。线程可以在等待数据时执行其他任务。
然后是多路复用的概念。
多路复用:多路复用是一种处理多个 I/O 事件的技术。它允许一个线程同时监视多个文件描述符或通道。当某个 I/O 事件准备好时,多路复用机制会通知应用程序,从而实现在单个线程中处理多个并发连接。
假设你现在是个大厨(炖个老母鸡汤,切点土豆丝/姜丝/葱丝):
- 同步/阻塞:你站在锅边,一直等到汤炖好,期间不能做其他事情,直到汤炖好才去处理其他任务。
- 同步/非阻塞:你不断地查看锅里的汤,看是否炖好。在检查的间隙,你可以处理其他任务,如切菜。但你需要不断地切换任务,确保汤炖好了就可以处理。
- 异步/信号驱动:你给锅安装一个传感器,当汤炖好时,传感器会发出信号提醒你。在此期间,你可以处理其他任务,而不用担心错过汤炖好的时机。
- 异步 I/O:你请了一个助手,让他负责炖汤。当汤炖好时,助手会通知你。你可以专心处理其他任务,而无需关心炖汤的过程。
这下清楚了吧?
### 02、select/epoll/poll/pselect/fd
还有涉及到 Unix 的`select/epoll/poll/pselect/fd`这些关键字,没有相关基础的人看起来简直是天书,这里补充一下。
- select 是 Unix 系统中最早的 I/O 多路复用技术。它允许一个线程同时监视多个文件描述符(如[套接字](https://tobebetterjavaer.com/socket/socket.html)),并等待某个文件描述符上的 I/O 事件(如可读、可写或异常)。select 的主要问题是性能受限,特别是在处理大量文件描述符时。这是因为它使用一个位掩码来表示文件描述符集,每次调用都需要传递这个掩码,并在内核和用户空间之间进行复制。
- poll 是对 select 的改进。它使用一个文件描述符数组而不是位掩码来表示文件描述符集。这样可以避免 select 中的性能问题。然而,poll 仍然需要遍历整个文件描述符数组,以检查每个文件描述符的状态。因此,在处理大量文件描述符时,性能仍然受限。
- epoll 是 Linux 中的一种高性能 I/O 多路复用技术。它通过在内核中维护一个事件表来避免遍历文件描述符数组的性能问题。当某个文件描述符上的 I/O 事件发生时,内核会将该事件添加到事件表中。应用程序可以使用 epoll_wait 函数来获取已准备好的 I/O 事件,而无需遍历整个文件描述符集。这种方法大大提高了在大量并发连接下的性能。
- pselect 是 select 的一个变体,它支持更精确的超时控制和原子性信号处理。pselect 允许应用程序在等待 I/O 事件时阻塞信号,从而避免竞争条件。然而,与 select 相比,pselect 在性能方面没有太大改进。
- fd:这个关键字指的是 "file descriptor"(文件描述符),它是 Unix 系统中用于表示打开的文件、套接字和其他 I/O 对象的整数标识符。文件描述符在 I/O 多路复用中起着关键作用,因为它们允许应用程序同时监视多个 I/O 对象。
### 03、NIO
我在找资料的过程中也收藏了好多讲解 NIO 的资料,这篇内容就是**以初学的角度来理解 NIO**。也算是我这两天看 NIO 的一个总结吧。希望大家可以看了之后知道什么是 NIO,NIO 的核心知识点是什么,以及会使用 NIO~
> 声明:本文使用 JDK1.8
### 01、NIO 和传统 IO 在操作文件时的差异
JDK 1.4 中,`java.nio.*包`引入新的 Java I/O 库,其目的是**提高速度**。实际上,“旧”的 I/O 包已经使用 NIO**重新实现过,即使我们不显式的使用 NIO 编程,也能从中受益**
- nio 翻译成 no-blocking io 或者 new io 都无所谓啦,都说得通~
在《Java 编程思想》读到“即使我们不显式的使用 NIO 编程,也能从中受益”的时候,我是挺在意的,所以:我们**测试**一下使用 NIO 复制文件和[传统 IO](https://tobebetterjavaer.com/io/file-path.html) 复制文件的性能:
在《Java 编程思想》读到“即使我们不显式的使用 NIO 编程,也能从中受益”的时候,我是挺在意的,所以:我们**测试**一下使用 NIO 复制文件和[传统 IO 复制文件](https://tobebetterjavaer.com/io/file-path.html) 的性能:
```java
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class SimpleFileTransferTest {
// 使用传统的 I/O 方法传输文件
......@@ -152,7 +108,11 @@ public class SimpleFileTransferTest {
}
```
在我给出实际的结论之前,你是否会有这样的结论:
先解释一下这段代码,里面出现的 [RandomAccessFile](https://tobebetterjavaer.com/io/file-path.html) 我们之前讲过,FileChannel 是 Java NIO(New I/O)库中的一个类,它提供了对文件的高效 I/O 操作,支持随机访问文件,允许在文件的任意位置进行读写操作。
与 RandomAccessFile 不同,FileChannel 使用了[缓冲区(ByteBuffer)](https://tobebetterjavaer.com/nio/buffer-channel.html)进行数据传输。
好,在我给出实际的结论之前,你是否会有这样的结论:
- 对于较小的文件,NIO 和普通 IO 之间的性能差异可能不会非常明显,因为文件本身较小,复制过程较快。
- 对于较大的文件,使用 NIO 的性能可能会明显优于普通 IO。因为 NIO 使用了更高效的缓冲区和通道机制,可以在内存中进行更快的数据传输。
......@@ -180,21 +140,15 @@ NIO(New I/O)的设计目标是解决传统 I/O(BIO,Blocking I/O)在处
②、NIO 支持 I/O 多路复用,这意味着一个线程可以同时监视多个通道(如套接字),并在 I/O 事件(如可读、可写)准备好时处理它们。这大大提高了网络传输中的性能,因为单个线程可以高效地管理多个并发连接。操作文件时这个优势也无法提现出来。
③、NIO 提供了 ByteBuffer 类,可以高效地管理缓冲区。这在网络传输中很重要,因为数据通常是以字节流的形式传输。操作文件的时候,虽然也有缓冲区,但优势仍然不够明显。
③、NIO 提供了 [ByteBuffer 类](https://tobebetterjavaer.com/nio/buffer-channel.html),可以高效地管理缓冲区。这在网络传输中很重要,因为数据通常是以字节流的形式传输。操作文件的时候,虽然也有缓冲区,但优势仍然不够明显。
### 04、传统 IO 和 NIO 实际的性能测试
### 02、NIO 和传统 IO 在网络传输中的差异
来看服务器端代码的差别。
IO,用的[套接字](https://tobebetterjavaer.com/socket/socket.html),代码比较简单,我就不加注释了,之前学过,应该都能看得懂,用 while 循环监听客户端 Socket:
```java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class IOServer {
public static void main(String[] args) {
try {
......@@ -223,15 +177,6 @@ public class IOServer {
NIO,这部分我加上注释,主要用到的是 ServerSocketChannel 和 Selector:
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NIOServer {
public static void main(String[] args) {
try {
......@@ -291,7 +236,7 @@ public class NIOServer {
}
```
上面的代码创建了一个基于 Java NIO 的简单 TCP 服务器。它使用 ServerSocketChannel 和 Selector 实现了非阻塞 I/O 和 I/O 多路复用。服务器循环监听事件,当有新的连接请求时,接受连接并将新的 SocketChannel 注册到 Selector,关注 OP_READ 事件。当有数据可读时,从 SocketChannel 中读取数据并写入 ByteBuffer,然后将数据从 ByteBuffer 写回到 SocketChannel。
上面的代码创建了一个基于 Java NIO 的简单 TCP 服务器。它使用 [ServerSocketChannel 和 Selector(后面会讲)](https://tobebetterjavaer.com/nio/buffer-channel.html) 实现了非阻塞 I/O 和 I/O 多路复用。服务器循环监听事件,当有新的连接请求时,接受连接并将新的 SocketChannel 注册到 Selector,关注 OP_READ 事件。当有数据可读时,从 SocketChannel 中读取数据并写入 ByteBuffer,然后将数据从 ByteBuffer 写回到 SocketChannel。
这里简单说一下 [Socket 和 ServerSocket](https://tobebetterjavaer.com/socket/socket.html),以及 ServerSocketChannel 和 SocketChannel。
......@@ -302,29 +247,18 @@ Socket 和 ServerSocket 是传统的阻塞式 I/O 编程方式,用于建立和
在传统阻塞式 I/O 编程中,每个连接都需要一个单独的线程进行处理,这导致了在高并发场景下的性能问题。在接下来的客户端测试用例中会看到。
为了解决传统阻塞式 I/O 的性能问题,Java NIO 引入了 ServerSocketChannel 和 SocketChannel。它们是非阻塞 I/O,可以在单个线程中处理多个连接。
为了解决传统阻塞式 I/O 的性能问题,Java NIO 引入了 [ServerSocketChannel 和 SocketChannel](https://tobebetterjavaer.com/nio/network-connect.html)。它们是非阻塞 I/O,可以在单个线程中处理多个连接。
- ServerSocketChannel:类似于 ServerSocket,表示服务器端套接字通道。它负责监听客户端连接请求,并可以设置为非阻塞模式,这意味着在等待客户端连接请求时不会阻塞线程。
- SocketChannel:类似于 Socket,表示客户端套接字通道。它负责与服务器端建立连接并进行数据的读写。SocketChannel 也可以设置为非阻塞模式,在读写数据时不会阻塞线程。
再来说一下 Selector
再来简单说一下 [Selector](https://tobebetterjavaer.com/nio/buffer-channel.html),后面会再细讲
Selector 是 Java NIO 中的一个关键组件,用于实现 I/O 多路复用。它允许在单个线程中同时监控多个 ServerSocketChannel 和 SocketChannel,并通过 SelectionKey 标识关注的事件。当某个事件发生时,Selector 会将对应的 SelectionKey 添加到已选择的键集合中。通过使用 Selector,可以在单个线程中同时处理多个连接,从而有效地提高 I/O 操作的性能,特别是在高并发场景下。
Selector 是 Java NIO 中的一个关键组件,用于实现 [I/O 多路复用](https://tobebetterjavaer.com/nio/moxing.html)。它允许在单个线程中同时监控多个 ServerSocketChannel 和 SocketChannel,并通过 SelectionKey 标识关注的事件。当某个事件发生时,Selector 会将对应的 SelectionKey 添加到已选择的键集合中。通过使用 Selector,可以在单个线程中同时处理多个连接,从而有效地提高 I/O 操作的性能,特别是在高并发场景下。
客户端测试用例:
```java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TestClient {
public static void main(String[] args) throws InterruptedException {
int clientCount = 10000;
......@@ -395,6 +329,14 @@ public class TestClient {
这说明 NIO 在网络传输中的性能确实要优于传统 IO 的。
### 03、小结
本篇内容主要讲了 NIO(New IO)和传统 IO 之间的差异,包括 IO 模型、操作文件、网络传输等方面。
- 传统 I/O 采用阻塞式模型,线程在 I/O 操作期间无法执行其他任务。NIO 使用非阻塞模型,允许线程在等待 I/O 时执行其他任务,通过选择器(Selector)监控多个通道(Channel)上的 I/O 事件,提高性能和可伸缩性。
- 传统 I/O 使用基于字节流或字符流的类(如 FileInputStream、BufferedReader 等)进行文件读写。NIO 使用通道(Channel)和缓冲区(Buffer)进行文件操作,NIO 在性能上的优势并不大。
- 传统 I/O 使用 Socket 和 ServerSocket 进行网络传输,存在阻塞问题。NIO 提供了 SocketChannel 和 ServerSocketChannel,支持非阻塞网络传输,提高了并发处理能力。
---
最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)
......
---
title: 聊聊 Java 中的Paths、Files
title: 聊聊 Java NIO 中的Paths 和 Files - 沉默王二 - java进阶之路
shortTitle: Paths和Files
category:
- Java核心
tag:
- Java IO
description: Java程序员进阶之路,小白的零基础Java教程,详解 File、Path、Paths、Files,操作文件不再难
description: Paths 和 Files 是 Java NIO 中的两个核心类。Paths 提供了一系列静态方法,用于操作路径(Path 对象)。Files 类提供了丰富的文件操作方法,如文件的创建、删除、移动、复制、读取和写入等。Files 还支持文件遍历(如 walkFileTree 方法),可以处理文件目录树。
author: 沉默王二
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,Java IO,file,paths,files,path,java文件,java目录,java文件增删改查,java file,java paths,java files
content: Java,nio,paths,files,path
---
# 12.4 PathsFiles
# 12.4 PathsFiles
Paths和Files在 Java 7 的时候引入,作为对 `java.io.File`的补充和改进。
Paths 和 Files 在 Java 7 的时候引入,作为对 [`java.io.File` 类](https://tobebetterjavaer.com/io/file-path.html)的补充和改进。
### Paths 类
Paths类主要用于操作文件和目录路径。它提供了一些静态方法,用于创建`java.nio.file.Path`实例,代表文件系统中的路径。
Paths 类主要用于操作文件和目录路径。它提供了一些静态方法,用于创建`java.nio.file.Path`实例,代表文件系统中的路径。
下面是 Paths 的一个示例。
......@@ -30,7 +31,7 @@ Path path = Paths.get("example.txt");
Path absolutePath = Paths.get("/home/user/example.txt");
```
java.nio.file.Path接口在Java NIO.2中代表一个文件系统中的路径。它提供了一系列方法来操作和查询路径。
java.nio.file.Path 接口在 Java NIO.2 中代表一个文件系统中的路径。它提供了一系列方法来操作和查询路径。
![](https://cdn.tobebetterjavaer.com/stutymore/paths-files-20230404181334.png)
......@@ -66,7 +67,7 @@ Path targetPath = Paths.get("/docs/imgs/itwanger");
Path relativePath = basePath.relativize(targetPath);
System.out.println("Relative path: " + relativePath);
```
### Files 类
`java.nio.file.Files`类提供了大量静态方法,用于处理文件系统中的文件和目录。这些方法包括文件的创建、删除、复制、移动等操作,以及读取和设置文件属性。
......@@ -182,7 +183,7 @@ StandardOpenOption 枚举类提供了以下几个选项:
- READ:以读取模式打开文件。
- WRITE:以写入模式打开文件。
- APPEND:以追加模式打开文件。
- TRUNCATE_EXISTING:在打开文件时,截断文件的内容,使其长度为0。仅适用于 WRITE 或 APPEND 模式。
- TRUNCATE_EXISTING:在打开文件时,截断文件的内容,使其长度为 0。仅适用于 WRITE 或 APPEND 模式。
- CREATE:当文件不存在时创建文件。如果文件已存在,则打开文件。
- CREATE_NEW:当文件不存在时创建文件。如果文件已存在,抛出 FileAlreadyExistsException。
- DELETE_ON_CLOSE:在关闭通道时删除文件。
......@@ -291,10 +292,10 @@ public class WalkFileTreeExample {
其中,FileVisitResult 枚举包含以下四个选项:
* CONTINUE : 继续
* TERMINATE : 终止
* SKIP\_SIBLINGS : 跳过兄弟节点,然后继续
* SKIP\_SUBTREE : 跳过子树(不访问此目录的条目),然后继续,仅在 preVisitDirectory 方法返回时才有意义,除此以外和 CONTINUE 相同。
- CONTINUE : 继续
- TERMINATE : 终止
- SKIP_SIBLINGS : 跳过兄弟节点,然后继续
- SKIP_SUBTREE : 跳过子树(不访问此目录的条目),然后继续,仅在 preVisitDirectory 方法返回时才有意义,除此以外和 CONTINUE 相同。
#### 搜索文件
......@@ -358,10 +359,10 @@ public class FindFileWithWalkFileTree {
Paths 和 Files 是 Java NIO 中的两个核心类。Paths 提供了一系列静态方法,用于操作路径(Path 对象)。它可以将字符串或 URI 转换为 Path 对象,方便后续操作。Files 类提供了丰富的文件操作方法,如文件的创建、删除、移动、复制、读取和写入等。这些方法支持各种选项和属性,如覆盖、保留属性和符号链接处理。Files 还支持文件遍历(如 walkFileTree 方法),可以处理文件目录树。总之,Paths 和 Files 为文件和目录操作提供了简洁、高效的方法。
---------
---
最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)
最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)
微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。
![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png)
\ No newline at end of file
![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册