关注

Java本地I/O编程终极指南

这是一份非常详细且权威的 Java 本地 I/O 编程指南,涵盖Java 本地 I/O 编程的各个方面,并提供必要的实战代码。


目录

  1. 背景与前世今生
    • 1.1 什么是 IO?
    • 1.2 Java IO 的演变历程
    • 1.3 Java NIO 的诞生背景
  2. 基础知识
    • 2.1 流 (Stream) 的概念
    • 2.2 字节流 vs 字符流
    • 2.3 输入流 vs 输出流
    • 2.4 节点流 vs 处理流 (包装流)
    • 2.5 标准输入/输出/错误
    • 2.6 File 类
  3. 核心知识点 (Java IO & NIO)
    • 3.1 Java IO (java.io)
      • 3.1.1 核心类解读 (InputStream, OutputStream, Reader, Writer)
      • 3.1.2 常用节点流 (FileInputStream, FileOutputStream, FileReader, FileWriter)
      • 3.1.3 常用处理流 (BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter, ObjectInputStream, ObjectOutputStream, PrintWriter)
      • 3.1.4 序列化与反序列化
    • 3.2 Java NIO (java.nio)
      • 3.2.1 核心概念 (Buffer, Channel, Selector)
      • 3.2.2 Buffer 详解 (属性、类型、操作)
      • 3.2.3 Channel 详解 (FileChannel, SocketChannel, ServerSocketChannel, DatagramChannel)
      • 3.2.4 Selector 与非阻塞 IO (Reactor 模式)
      • 3.2.5 Path 与 Files (替代 File 类)
  4. 调优与性能优化
    • 4.1 缓冲区 (Buffering) 的重要性
    • 4.2 减少系统调用次数
    • 4.3 内存映射文件 (MappedByteBuffer)
    • 4.4 直接缓冲区 (Direct Buffer) vs 堆缓冲区 (Heap Buffer)
    • 4.5 文件锁 (FileLock)
    • 4.6 零拷贝技术
  5. 优缺点横向对比
    • 5.1 Java IO (java.io) vs Java NIO (java.nio)
      • 模型 (阻塞 vs 非阻塞/事件驱动)
      • 性能 (小文件 vs 大文件 vs 高并发)
      • 易用性
    • 5.2 Java NIO 与 Netty
  6. 算法
    • 6.1 文件遍历算法 (递归 vs NIO Files.walk)
    • 6.2 文件内容查找 (逐行读取 vs 内存映射)
    • 6.3 大文件处理策略 (分块读取/写入)
    • 6.4 文件校验 (MD5, SHA)
  7. 避坑指南
    • 7.1 资源泄漏 (close() 方法调用,Try-with-Resources)
    • 7.2 字符编码问题
    • 7.3 文件路径问题 (相对路径 vs 绝对路径)
    • 7.4 文件锁的正确使用
    • 7.5 MappedByteBuffer 的释放问题
    • 7.6 Selector 的空轮询问题
  8. 应用场景
    • 8.1 配置文件读写
    • 8.2 日志文件记录
    • 8.3 文件上传/下载
    • 8.4 数据持久化 (序列化对象)
    • 8.5 本地数据库交互 (如 SQLite)
    • 8.6 高性能网络服务器 (结合 NIO)
  9. 并发与高可用
    • 9.1 Java IO 的并发限制 (阻塞模型)
    • 9.2 Java NIO 的高并发能力
    • 9.3 线程池与 IO 操作
    • 9.4 文件锁 (FileLock) 与并发控制
    • 9.5 高可用文件存储策略 (RAID, 分布式文件系统)
  10. QPS & TPS
    • 10.1 定义
    • 10.2 影响本地 IO QPS/TPS 的因素
      • 磁盘类型 (HDD, SSD)
      • 文件大小
      • 缓冲区大小
      • IO 模型 (BIO vs NIO)
      • 是否使用零拷贝
    • 10.3 测试方法 (JMH)
  11. Spring Boot 项目实战
    • 11.1 配置文件读取 (@Value, @ConfigurationProperties)
    • 11.2 文件上传实现 (MultipartFile)
    • 11.3 文件下载实现 (ResponseEntity)
    • 11.4 日志记录 (Logback, Log4j2)
    • 11.5 使用 Spring Content 管理内容存储
    • 11.6 集成 NIO 进行高性能文件处理 (示例)
  12. 总结

1. 背景与前世今生

1.1 什么是 IO?

IO (Input/Output),即输入/输出,是指程序与外部世界(如文件系统、网络连接、用户输入设备、输出设备等)进行数据交换的过程。在 Java 中,主要关注的是文件 IO网络 IO。本指南重点讨论本地文件 IO

1.2 Java IO 的演变历程

  • Java 1.0 (1996): 引入了最初的 java.io 包。核心是流 (Stream) 的概念,分为字节流 (InputStream, OutputStream) 和字符流 (Reader, Writer)。这是阻塞式 (Blocking IO) 模型:当一个线程执行读或写操作时,如果数据没有准备好(如磁盘未响应),该线程会被挂起,直到操作完成。这种模型简单易用,但在高并发场景下需要大量线程支持,资源消耗大。
  • Java 1.4 (2002): 引入了 java.nio (New IO) 包。核心是 Buffer (缓冲区), Channel (通道)Selector (选择器)。NIO 支持 非阻塞模式 (Non-blocking Mode) 和基于 事件驱动 (Event-driven)IO 多路复用 (Multiplexing) 模型 (使用 Selector)。这种模型允许一个线程管理多个 Channel,显著提高了在高并发连接下的性能和可伸缩性。
  • Java 7 (2011): 引入了 java.nio.file 包,提供了 PathFiles 等工具类,极大地简化了文件操作,功能也更强大(如文件属性、符号链接、目录遍历等),是对 java.io.File 的现代化替代。
  • 后续发展: Java 7 还引入了 AsynchronousFileChannel,提供了异步文件 IO 操作。Java NIO 也成为构建高性能网络框架(如 Netty)的基础。

1.3 Java NIO 的诞生背景

传统的阻塞式 IO (BIO) 模型在高并发场景下(如需要同时处理成千上万个网络连接)存在瓶颈:

  • 线程开销大: 每个连接需要一个线程,线程的创建、销毁、上下文切换开销巨大。
  • 资源浪费: 线程在等待 IO 操作完成时处于阻塞状态,CPU 资源被闲置。

NIO 的非阻塞和 IO 多路复用模型旨在解决这些问题:

  • 非阻塞: 线程发起 IO 请求后立即返回,不会被阻塞。数据准备好后,线程再进行处理。
  • IO 多路复用: 一个线程(通过 Selector)可以监听多个 Channel 上的事件(读就绪、写就绪、连接就绪等)。当有事件发生时,线程才进行相应处理,大大提高了线程利用率。

2. 基础知识

2.1 流 (Stream) 的概念

流是一个有序的字节序列字符序列,具有方向性(输入或输出)。Java IO 将数据的读写抽象为流的操作,屏蔽了底层设备的差异。

2.2 字节流 vs 字符流

  • 字节流 (InputStream, OutputStream): 以字节为单位(8位)读写数据。适用于所有类型的数据(图片、音频、视频、二进制文件等)。
  • 字符流 (Reader, Writer): 以字符为单位(16位 Unicode)读写数据。专为处理文本数据设计,能自动处理字符编码(如 UTF-8, GBK)。

选择原则: 操作二进制数据(如图片)用字节流;操作文本数据用字符流。

2.3 输入流 vs 输出流

  • 输入流 (InputStream, Reader): 从数据源(如文件)读取数据到程序。
  • 输出流 (OutputStream, Writer): 将程序中的数据写入到目的地(如文件)。

2.4 节点流 vs 处理流 (包装流)

  • 节点流: 直接与特定的数据源或目的地(如文件、内存数组、管道)相连。例如 FileInputStream, FileOutputStream, FileReader, FileWriter
  • 处理流 (包装流): 对已有的流进行包装,提供额外的功能(如缓冲、转换编码、对象序列化)。例如 BufferedInputStream, BufferedReader, InputStreamReader, ObjectInputStream。处理流是装饰器模式的应用。
// 节点流直接操作文件
FileInputStream fis = new FileInputStream("source.txt");
FileOutputStream fos = new FileOutputStream("dest.txt");

// 处理流包装节点流,提供缓冲功能 (提高效率)
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

// 处理流包装字节流,提供按字符读取功能 (并指定编码)
BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));

2.5 标准输入/输出/错误

Java 提供了三个标准的流对象:

  • System.in: 标准输入流 (通常是键盘输入),类型是 InputStream
  • System.out: 标准输出流 (通常是控制台),类型是 PrintStream
  • System.err: 标准错误输出流 (通常是控制台),类型是 PrintStream
// 从标准输入读取 (通常包装成 BufferedReader)
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput = stdIn.readLine();

// 输出到标准输出
System.out.println("Hello, World!");

// 输出到标准错误
System.err.println("An error occurred!");

2.6 File 类

java.io.File 类代表文件和目录路径名的抽象表示。它可以用于:

  • 创建文件/目录
  • 删除文件/目录
  • 重命名文件/目录
  • 判断文件/目录是否存在
  • 获取文件属性(大小、最后修改时间等)
  • 列出目录内容
File file = new File("test.txt");
if (file.exists()) {
    System.out.println("File name: " + file.getName());
    System.out.println("File size: " + file.length() + " bytes");
    System.out.println("Is directory: " + file.isDirectory());
    System.out.println("Last modified: " + new Date(file.lastModified()));
} else {
    file.createNewFile(); // 创建文件
}

注意: Java 7 引入了更强大的 java.nio.file.Pathjava.nio.file.Files,推荐在新代码中使用它们替代 File

3. 核心知识点 (Java IO & NIO)

3.1 Java IO (java.io)

3.1.1 核心类解读
  • InputStream (抽象类): 所有字节输入流的父类。
    • 核心方法:
      • int read(): 读取一个字节,返回读取的字节值(0-255),如果到达流末尾返回 -1。
      • int read(byte[] b): 读取最多 b.length 个字节到数组 b,返回实际读取的字节数,如果到达流末尾返回 -1。
      • int read(byte[] b, int off, int len): 读取最多 len 个字节到数组 b 从偏移 off 开始的位置。
      • void close(): 关闭流,释放资源。必须调用! (推荐使用 try-with-resources)
  • OutputStream (抽象类): 所有字节输出流的父类。
    • 核心方法:
      • void write(int b): 写入一个字节(低8位)。
      • void write(byte[] b): 写入整个字节数组 b
      • void write(byte[] b, int off, int len): 写入字节数组 b 从偏移 off 开始的 len 个字节。
      • void flush(): 刷新输出流,强制写出缓冲区中的数据(如果流支持缓冲)。
      • void close(): 关闭流,释放资源。必须调用! (推荐使用 try-with-resources)
  • Reader (抽象类): 所有字符输入流的父类。
    • 核心方法:
      • int read(): 读取一个字符,返回读取的字符值(0-65535),如果到达流末尾返回 -1。
      • int read(char[] cbuf): 读取最多 cbuf.length 个字符到数组 cbuf,返回实际读取的字符数,如果到达流末尾返回 -1。
      • int read(char[] cbuf, int off, int len): 读取最多 len 个字符到数组 cbuf 从偏移 off 开始的位置。
      • void close(): 关闭流,释放资源。
  • Writer (抽象类): 所有字符输出流的父类。
    • 核心方法:
      • void write(int c): 写入一个字符。
      • void write(char[] cbuf): 写入整个字符数组 cbuf
      • void write(char[] cbuf, int off, int len): 写入字符数组 cbuf 从偏移 off 开始的 len 个字符。
      • void write(String str): 写入整个字符串 str
      • void write(String str, int off, int len): 写入字符串 str 从偏移 off

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/suodasheng/article/details/157332228

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--