11# Java IO
22
3- 原作者github: https://github.com/CyC2018/Interview-Notebook
4-
5- PDF离线制作github: https://github.com/sjsdfg/Interview-Notebook-PDF
6-
7- 希望各位不吝star
8-
3+ 原作者github: https://github.com/sjsdfg/Interview-Notebook-PDF
94
5+ PDF制作github: https://github.com/sjsdfg/Interview-Notebook-PDF
106
117# 一、概览
128
@@ -35,7 +31,7 @@ public static void listAllFiles(File dir)
3531 System . out. println(dir. getName());
3632 return ;
3733 }
38- for (File file : Objects . requireNonNull( dir. listFiles() )) {
34+ for (File file : dir. listFiles()) {
3935 listAllFiles(file);
4036 }
4137}
@@ -51,8 +47,9 @@ public static void copyFile(String src, String dist) throws IOException
5147 FileInputStream in = new FileInputStream (src);
5248 FileOutputStream out = new FileOutputStream (dist);
5349 byte [] buffer = new byte [20 * 1024 ];
54- /* read() 最多读取 buffer.length 个字节
55- 返回的是实际读取的个数,返回 -1 的时候表示读到 eof,即文件尾 */
50+ // read() 最多读取 buffer.length 个字节
51+ // 返回的是实际读取的个数
52+ // 返回 -1 的时候表示读到 eof,即文件尾
5653 while (in. read(buffer, 0 , buffer. length) != - 1 ) {
5754 out. write(buffer);
5855 }
@@ -68,7 +65,7 @@ Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStrea
6865实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。
6966
7067``` java
71- FileInputStream fileInputStream = new FileInputStream (" file/1.txt " );
68+ FileInputStream fileInputStream = new FileInputStream (filePath );
7269BufferedInputStream bufferedInputStream = new BufferedInputStream (fileInputStream);
7370```
7471
@@ -78,22 +75,25 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作,
7875
7976不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。
8077
81- - InputStreamReader 实现从文本文件的字节流解码成字符流 ;
82- - OutputStreamWriter 实现字符流编码成为文本文件的字节流 。
78+ - InputStreamReader 实现从字节流解码成字符流 ;
79+ - OutputStreamWriter 实现字符流编码成为字节流 。
8380
8481逐行输出文本文件的内容:
8582
8683``` java
87- FileReader fileReader = new FileReader (" file/1.txt" );
88- BufferedReader bufferedReader = new BufferedReader (fileReader);
89- String line;
90- while ((line = bufferedReader. readLine()) != null ) {
91- System . out. println(line);
84+ public static void readFileContent(String filePath) throws IOException
85+ {
86+ FileReader fileReader = new FileReader (filePath);
87+ BufferedReader bufferedReader = new BufferedReader (fileReader);
88+ String line;
89+ while ((line = bufferedReader. readLine()) != null ) {
90+ System . out. println(line);
91+ }
92+ // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象
93+ // 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法
94+ // 因此只要一个 close() 调用即可
95+ bufferedReader. close();
9296}
93- /* 装饰者模式使得 BufferedReader 组合了一个 Reader 对象
94- 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法
95- 因此只要一个 close() 调用即可 */
96- bufferedReader. close();
9797```
9898
9999编码就是把字符转换为字节,而解码是把字节重新组合成字符。
@@ -202,8 +202,10 @@ InetAddress.getByAddress(byte[] address);
202202public static void main(String [] args) throws IOException
203203{
204204 URL url = new URL (" http://www.baidu.com" );
205- InputStream is = url. openStream(); /* 字节流 */
206- InputStreamReader isr = new InputStreamReader (is, " utf-8" ); /* 字符流 */
205+ // 字节流
206+ InputStream is = url. openStream();
207+ // 字符流
208+ InputStreamReader isr = new InputStreamReader (is, " utf-8" );
207209 BufferedReader br = new BufferedReader (isr);
208210 String line = br. readLine();
209211 while (line != null ) {
@@ -239,7 +241,7 @@ public static void main(String[] args) throws IOException
239241
240242I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
241243
242- 面向流的 I/O 一次处理一个字节数据, 一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。
244+ 面向流的 I/O 一次处理一个字节数据: 一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。
243245
244246面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
245247
@@ -311,7 +313,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
311313``` java
312314public static void fastCopy(String src, String dist) throws IOException
313315{
314- FileInputStream fin = new FileInputStream (src); /* 获得源文件的输入字节流 */
316+ FileInputStream fin = new FileInputStream (src); /* 获取源文件的输入字节流 */
315317 FileChannel fcin = fin. getChannel(); /* 获取输入字节流的文件通道 */
316318 FileOutputStream fout = new FileOutputStream (dist); /* 获取目标文件的输出字节流 */
317319 FileChannel fcout = fout. getChannel(); /* 获取输出字节流的通道 */
@@ -330,10 +332,16 @@ public static void fastCopy(String src, String dist) throws IOException
330332
331333## 选择器
332334
333- 一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去检查多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。
335+ NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。
336+
337+ NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。
338+
339+ 通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。
334340
335341因为创建和切换线程的开销很大,因此使用一个线程来处理多个事件而不是一个线程处理一个事件具有更好的性能。
336342
343+ 应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。
344+
337345<div align =" center " > <img src =" https://github.com/CyC2018/Interview-Notebook/raw/master/pics/4d930e22-f493-49ae-8dff-ea21cd6895dc.png " /> </div ><br >
338346
339347### 1. 创建选择器
@@ -350,7 +358,7 @@ ssChannel.configureBlocking(false);
350358ssChannel. register(selector, SelectionKey . OP_ACCEPT );
351359```
352360
353- 通道必须配置为非阻塞模式,否则使用选择器就没有任何意义了,因为如果通道在某个事件上被阻塞,那么服务器就不能响应其它时间 ,必须等待这个事件处理完毕才能去处理其它事件,显然这和选择器的作用背道而驰。
361+ 通道必须配置为非阻塞模式,否则使用选择器就没有任何意义了,因为如果通道在某个事件上被阻塞,那么服务器就不能响应其它事件 ,必须等待这个事件处理完毕才能去处理其它事件,显然这和选择器的作用背道而驰。
354362
355363在将通道注册到选择器上时,还需要指定要注册的具体事件,主要有以下几类:
356364
@@ -380,7 +388,7 @@ int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
380388int num = selector. select();
381389```
382390
383- 使用 select() 来监听事件到达 ,它会一直阻塞直到有至少一个事件到达。
391+ 使用 select() 来监听到达的事件 ,它会一直阻塞直到有至少一个事件到达。
384392
385393### 4. 获取到达的事件
386394
@@ -422,9 +430,10 @@ while (true) {
422430## 套接字 NIO 实例
423431
424432``` java
425- public class NIOServer {
426-
427- public static void main (String [] args ) throws IOException {
433+ public class NIOServer
434+ {
435+ public static void main (String [] args ) throws IOException
436+ {
428437 Selector selector = Selector . open();
429438
430439 ServerSocketChannel ssChannel = ServerSocketChannel . open();
@@ -482,9 +491,10 @@ public class NIOServer {
482491```
483492
484493``` java
485- public class NIOClient {
486-
487- public static void main (String [] args ) throws IOException {
494+ public class NIOClient
495+ {
496+ public static void main (String [] args ) throws IOException
497+ {
488498 Socket socket = new Socket (" 127.0.0.1" , 8888 );
489499 OutputStream out = socket. getOutputStream();
490500 String s = " hello world" ;
@@ -498,10 +508,6 @@ public class NIOClient {
498508
499509内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
500510
501- 只有文件中实际读取或者写入的部分才会映射到内存中。
502-
503- 现代操作系统一般会根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制只不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。
504-
505511向内存映射文件写入可能是危险的,仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
506512
507513下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,您可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。
@@ -514,8 +520,8 @@ MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
514520
515521NIO 与普通 I/O 的区别主要有以下两点:
516522
517- - NIO 是非阻塞的。应当注意,FileChannel 不能切换到非阻塞模式,套接字 Channel 可以。
518- - NIO 面向块,I/O 面向流。
523+ - NIO 是非阻塞的
524+ - NIO 面向块,I/O 面向流
519525
520526# 八、参考资料
521527
@@ -528,4 +534,6 @@ NIO 与普通 I/O 的区别主要有以下两点:
528534- [ Decorator Design Pattern] ( http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document )
529535- [ Socket Multicast] ( http://labojava.blogspot.com/2012/12/socket-multicast.html )
530536
537+
538+ ---
531539github: https://github.com/sjsdfg/Interview-Notebook-PDF
0 commit comments