Java IO 和 NIO 的基本概念和 API

一、 Java IO (Blocking IO)

  1. 基本概念:

    • Java IO 是 Java 平台提供的用于进行输入和输出操作的 API。
    • Java IO 基于 流 (Stream) 的模型,数据像水流一样从一个地方流向另一个地方。
    • Java IO 主要是 阻塞式 I/O (Blocking I/O),即线程在执行 I/O 操作时会被阻塞,直到操作完成。
    • 传统IO指的是java.io包下的部分组件(File, InputStream, OutputStream, Reader, Writer)。
  2. IO 流的分类:

    • 按数据传输方向:

      • 输入流 (Input Stream): 用于从数据源读取数据(例如,从文件、网络连接、键盘等)。 以 InputStreamReader 作为基类。
      • 输出流 (Output Stream): 用于将数据写入到目标(例如,写入到文件、网络连接、控制台等)。 以 OutputStreamWriter 作为基类。
    • 按数据传输单位:

      • 字节流 (Byte Stream): 以字节 (8 bits) 为单位进行数据传输。 以 InputStreamOutputStream 作为基类。 适用于处理二进制数据(例如,图片、音频、视频等)。
      • 字符流 (Character Stream): 以字符 (16 bits) 为单位进行数据传输。 以 ReaderWriter 作为基类。 适用于处理文本数据。
  3. 核心类和接口:

    • InputStream (字节输入流):

      • FileInputStream: 从文件中读取字节。
      • ByteArrayInputStream: 从字节数组中读取字节。
      • ObjectInputStream: 从对象流中读取对象。
      • BufferedInputStream: 带缓冲的字节输入流,提高读取效率。
    • OutputStream (字节输出流):

      • FileOutputStream: 向文件中写入字节。
      • ByteArrayOutputStream: 向字节数组中写入字节。
      • ObjectOutputStream: 向对象流中写入对象。
      • BufferedOutputStream: 带缓冲的字节输出流,提高写入效率。
    • Reader (字符输入流):

      • FileReader: 从文件中读取字符。
      • CharArrayReader: 从字符数组中读取字符。
      • BufferedReader: 带缓冲的字符输入流,提高读取效率。
      • InputStreamReader: 将字节输入流转换为字符输入流(需要指定字符编码)。
    • Writer (字符输出流):

      • FileWriter: 向文件中写入字符。
      • CharArrayWriter: 向字符数组中写入字符。
      • BufferedWriter: 带缓冲的字符输出流,提高写入效率。
      • OutputStreamWriter: 将字节输出流转换为字符输出流(需要指定字符编码)。
    • File: 表示文件或目录的抽象表示。

  4. IO 操作流程 (以读取文件为例):

    1. 创建 File 对象: 指定要读取的文件路径。
    2. 创建 FileInputStream 对象:File 对象作为参数传递给 FileInputStream 的构造方法,创建一个 FileInputStream 对象。
    3. 创建 BufferedInputStream 对象 (可选):FileInputStream 对象作为参数传递给 BufferedInputStream 的构造方法,创建一个 BufferedInputStream 对象,提高读取效率。
    4. 读取数据: 使用 read() 方法从输入流中读取数据。
    5. 关闭流: 在完成读取操作后,务必关闭输入流,释放资源(先关闭 BufferedInputStream,再关闭 FileInputStream)。
  5. 代码示例 (读取文件内容):

    java 复制代码
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class IOExample {
        public static void main(String[] args) {
            File file = new File("test.txt"); // 替换为你的文件路径
    
            try (FileInputStream fis = new FileInputStream(file);
                 BufferedInputStream bis = new BufferedInputStream(fis)) { // 使用 try-with-resources 语句,自动关闭流
    
                byte[] buffer = new byte[1024];
                int bytesRead;
    
                while ((bytesRead = bis.read(buffer)) != -1) {
                    // 处理读取到的数据
                    String data = new String(buffer, 0, bytesRead);
                    System.out.print(data);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

二、 Java NIO (Non-blocking IO)

  1. 基本概念:

    • Java NIO 是 Java 1.4 引入的一组新的 I/O API,旨在提供高性能、非阻塞的 I/O 操作。
    • NIO 使用 通道 (Channel)缓冲区 (Buffer) 的模型,而不是流。
    • NIO 主要是 非阻塞式 I/O (Non-blocking I/O),即线程在执行 I/O 操作时不会被阻塞,而是可以执行其他任务。
    • NIO 使用 选择器 (Selector) 来监听多个通道的事件,实现单线程管理多个连接。
  2. 核心组件:

    • 通道 (Channel):

      • 通道类似于流,但可以进行双向数据传输(既可以读取数据,也可以写入数据)。
      • 常见的通道:
        • FileChannel: 用于文件 I/O。
        • SocketChannel: 用于 TCP 网络 I/O (客户端)。
        • ServerSocketChannel: 用于 TCP 网络 I/O (服务器端)。
        • DatagramChannel: 用于 UDP 网络 I/O。
    • 缓冲区 (Buffer):

      • 缓冲区是用于存储数据的容器,本质上是一个字节数组 (ByteBuffer) 或字符数组 (CharBuffer)。
      • NIO 使用缓冲区进行数据传输,而不是直接从通道读取数据或向通道写入数据。
      • 常见的缓冲区:
        • ByteBuffer: 字节缓冲区。
        • CharBuffer: 字符缓冲区。
        • ShortBuffer: 短整型缓冲区。
        • IntBuffer: 整型缓冲区。
        • LongBuffer: 长整型缓冲区。
        • FloatBuffer: 浮点型缓冲区。
        • DoubleBuffer: 双精度浮点型缓冲区。
    • 选择器 (Selector):

      • 选择器允许单个线程监听多个通道的事件(例如连接建立、数据可读、数据可写等)。
      • 使用选择器可以避免为每个连接创建一个线程,从而提高并发性能。
  3. NIO 操作流程 (以读取 SocketChannel 数据为例):

    1. 创建 ServerSocketChannel 监听客户端连接。
    2. 创建 SocketChannel 接受客户端连接。
    3. SocketChannel 注册到 Selector 指定要监听的事件(例如 OP_READ, OP_WRITE, OP_CONNECT, OP_ACCEPT)。
    4. 创建 ByteBuffer 用于存储读取到的数据。
    5. 调用 selector.select() 方法: 阻塞等待有事件发生的通道。
    6. 获取就绪的通道: selector.selectedKeys() 返回所有就绪通道的集合。
    7. 处理事件: 遍历就绪通道的集合,根据不同的事件类型执行相应的操作(例如读取数据、写入数据)。
    8. 读取数据: 调用 channel.read(buffer) 从通道读取数据到缓冲区。
    9. 处理缓冲区数据: 从缓冲区读取数据并进行处理。
    10. 关闭通道和选择器: 在完成操作后,务必关闭通道和选择器,释放资源。
  4. 代码示例 (使用 SocketChannel 读取数据):

    java 复制代码
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    
    public class NIOExample {
        public static void main(String[] args) throws IOException {
            // 1. 创建 SocketChannel
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("www.example.com", 80));
            socketChannel.configureBlocking(false); // 设置为非阻塞模式
    
            // 2. 创建 ByteBuffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
    
            // 3. 从 Channel 读取数据到 Buffer
            int bytesRead = socketChannel.read(buffer);
    
            while (bytesRead > 0) {
                // 切换到读模式
                buffer.flip();
    
                // 4. 从 Buffer 读取数据
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
    
                // 清空 Buffer,准备下一次读取
                buffer.clear();
                bytesRead = socketChannel.read(buffer);
            }
    
            socketChannel.close();
        }
    }

三、 Java IO 与 NIO 的区别

特性 Java IO (Blocking IO) Java NIO (Non-blocking IO)
数据传输方式 基于流 (Stream) 基于通道 (Channel) 和缓冲区 (Buffer)
I/O 模型 阻塞式 I/O (Blocking I/O) 非阻塞式 I/O (Non-blocking I/O)
选择器 没有 有 (Selector)
API 简单易用 相对复杂,需要理解通道、缓冲区、选择器等概念
性能 性能较低 (高并发下) 性能较高 (高并发下)
线程模型 通常使用多线程模型 (每个连接一个线程) 通常使用单线程多路复用模型 (一个线程管理多个连接)
适用场景 低并发、连接数较少的应用,或者可以接受阻塞的场景 高并发、连接数较多的应用,需要高性能和非阻塞的场景,例如网络服务器、聊天服务器等

四、 选择哪种 I/O 模式?

  • 如果你的应用是低并发、连接数较少,并且可以接受阻塞,那么 Java IO 仍然是一个不错的选择,因为它简单易用。
  • 如果你的应用是高并发、连接数较多,并且对性能要求很高,那么应该使用 Java NIO。
  • 一般情况建议直接使用NIO模型,性能更好。

总结

Java IO 和 NIO 都是 Java 平台提供的用于进行输入和输出操作的 API。 Java IO 基于流的模型,使用简单但性能较低; Java NIO 基于通道和缓冲区的模型,提供高性能、非阻塞的 I/O 操作。

相关推荐
best_virtuoso3 分钟前
会话对象 HttpSession 一、HttpSession原理
java·前端
被程序耽误的胡先生21 分钟前
复杂sql
java·数据库·sql
m0_7482507424 分钟前
解决报错net.sf.jsqlparser.statement.select.SelectBody
java
JiaJunRun24 分钟前
Java集合体系结构面试题
java·开发语言·windows·学习·安全
五行星辰35 分钟前
Hutool - Setting:功能更强大的 Setting 配置文件和 Properties 封装
java·开发语言·后端
路在脚下@43 分钟前
数据库并发问题有那些以及解决办法
java·sql
m0_748251081 小时前
Spring Boot中的404错误:原因、影响及处理策略
java·spring boot·后端
m0_748240541 小时前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
啾啾Fun1 小时前
[java基础-JVM篇]1_JVM自动内存管理
java·开发语言·jvm
KuunNNn1 小时前
蓝桥杯试题:区间次方和(前缀和)
java·蓝桥杯