当前位置: 首页 > java >正文

11.Java I/O 流:文件读写与数据持久化​

        在 Java 编程世界里,I/O 流是实现数据输入输出的基础组件,广泛应用于文件操作、网络通信、数据持久化等场景。本文将深入剖析 Java I/O 流的核心概念,区分字节流与字符流的使用场景,结合文件读写、图片复制等实战案例,帮助开发者掌握数据存储与读取的关键技术。

 

一、Java I/O 流核心概念​

1. 流的分类​

        Java I/O 流按方向分为输入流(读取数据)和输出流(写入数据),按处理单元分为字节流和字符流:​

  • 字节流:以字节(byte,8 位)为单位处理数据,适用于二进制文件(如图片、视频、可执行文件),能够处理任意类型的数据。​
  • 字符流:以字符(char,16 位 Unicode)为单位处理数据,自动处理字符编码,适用于文本文件(如.txt、.properties)。​

2. 流的顶层抽象类​

字节流:​

  • 输入流顶层类:InputStream(抽象类,定义字节读取方法)​
  • 输出流顶层类:OutputStream(抽象类,定义字节写入方法)​

字符流:​

  • 输入流顶层类:Reader(抽象类,定义字符读取方法)​
  • 输出流顶层类:Writer(抽象类,定义字符写入方法)​

3. 流的装饰器模式​

        Java 通过装饰器模式增强流的功能,核心思想是通过包装类为基础流添加缓冲、编码转换、数据格式处理等功能。例如:​

  • BufferedInputStream/BufferedOutputStream:添加缓冲功能,减少磁盘 / 网络交互次数,提升读写效率。​
  • InputStreamReader/OutputStreamWriter:实现字节流与字符流的桥梁,处理字符编码转换。​
  • DataInputStream/DataOutputStream:支持基本数据类型(如int、String)的读写。​

 

二、字节流:处理二进制数据​

1. 文件字节流:FileInputStream与FileOutputStream​

使用场景:读取 / 写入二进制文件(如图片、音频、压缩包),适合处理非文本类型的数据。​

示例 1:单字节读取(原理演示,性能较低)

import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  /**  * 图片复制示例(单字节读取)  * 演示基本字节流操作,适合理解底层原理,但不推荐用于实际项目  */  
public class ImageCopyExample {  public static void main(String[] args) {  String sourcePath = "source.jpg";   // 源文件路径  String targetPath = "target.jpg";  // 目标文件路径  // try-with-resources自动关闭流,避免资源泄漏(Java 7+特性)  try (FileInputStream fis = new FileInputStream(sourcePath);  FileOutputStream fos = new FileOutputStream(targetPath)) {  int byteData;  // read()方法:读取一个字节,返回-1表示文件结束  while ((byteData = fis.read()) != -1) {  fos.write(byteData);  // 写入单个字节  }  System.out.println("图片复制完成(单字节模式)!");  } catch (IOException e) {  System.err.println("文件操作失败:" + e.getMessage());  e.printStackTrace();  }  }  
}  

示例 2:缓冲区读取(推荐实现,性能提升 10 倍 +)​

import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  /**  * 图片复制示例(缓冲区读取)  * 使用字节数组批量读写,减少系统调用次数  */  
public class BufferedImageCopyExample {  public static void main(String[] args) {  String sourcePath = "source.jpg";  String targetPath = "target.jpg";  byte[] buffer = new byte[8192];  // 8KB缓冲区(典型值,可根据内存调整)  try (FileInputStream fis = new FileInputStream(sourcePath);  FileOutputStream fos = new FileOutputStream(targetPath)) {  int bytesRead;  // read(byte[]):读取数据到缓冲区,返回实际读取的字节数  while ((bytesRead = fis.read(buffer)) != -1) {  fos.write(buffer, 0, bytesRead);  // 写入缓冲区中的有效数据  }  System.out.println("图片复制完成(缓冲区模式)!");  } catch (IOException e) {  System.err.println("文件操作失败:" + e.getMessage());  }  }  
}  

2. 缓冲字节流:BufferedInputStream与BufferedOutputStream​

原理:内部维护一个默认大小(通常 8KB)的缓冲区,批量读取 / 写入数据,减少与底层设备(如磁盘)的交互次数,显著提升性能。​

示例:缓冲流高效复制图片

import java.io.BufferedInputStream;  
import java.io.BufferedOutputStream;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  public class BufferedStreamExample {  public static void main(String[] args) {  String sourcePath = "source.jpg";  String targetPath = "target.jpg";  // 装饰器模式:将FileInputStream包装为BufferedInputStream  try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {  byte[] buffer = new byte[8192];  int bytesRead;  while ((bytesRead = bis.read(buffer)) != -1) {  bos.write(buffer, 0, bytesRead);  }  System.out.println("使用缓冲流复制完成,性能提升约10倍!");  } catch (IOException e) {  e.printStackTrace();  }  }  
}  

 

三、字符流:处理文本数据​

1. 文件字符流:FileReader与FileWriter​

使用场景:读取 / 写入文本文件(如日志、配置文件),自动处理字符编码(默认使用系统编码,可能导致乱码)。​

示例 1:单字符读取(原理演示)

import java.io.FileReader;  
import java.io.FileWriter;  
import java.io.IOException;  /**  * 文本文件复制示例(单字符读取)  * 注意:默认使用系统编码(如Windows的GBK),处理中文可能乱码  */  
public class TextCopyExample {  public static void main(String[] args) {  String sourcePath = "source.txt";  String targetPath = "target.txt";  try (FileReader reader = new FileReader(sourcePath);  FileWriter writer = new FileWriter(targetPath)) {  int character;  // read():读取一个字符(Unicode码点),返回-1表示文件结束  while ((character = reader.read()) != -1) {  writer.write(character);  // 写入单个字符  }  System.out.println("文本复制完成(单字符模式)!");  } catch (IOException e) {  e.printStackTrace();  }  }  
}  

示例 2:显式指定 UTF-8 编码(推荐)​

import java.io.BufferedReader;  
import java.io.BufferedWriter;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.OutputStreamWriter;  /**  * 文本文件复制(指定UTF-8编码)  * 解决中文乱码问题,支持跨平台文件读写  */  
public class EncodingExample {  public static void main(String[] args) {  String sourcePath = "source.txt";  String targetPath = "target.txt";  try (BufferedReader reader = new BufferedReader(  new InputStreamReader(new FileInputStream(sourcePath), "UTF-8"));  BufferedWriter writer = new BufferedWriter(  new OutputStreamWriter(new FileOutputStream(targetPath), "UTF-8"))) {  int character;  while ((character = reader.read()) != -1) {  writer.write(character);  }  System.out.println("UTF-8编码文本复制完成!");  } catch (IOException e) {  e.printStackTrace();  }  }  
}  

2. 缓冲字符流:BufferedReader与BufferedWriter​

优势:​

  • BufferedReader支持按行读取(readLine()方法),无需手动处理换行符。​
  • BufferedWriter支持高效写入,并提供平台无关的换行符(newLine()方法)。​

示例:按行读取文本文件

import java.io.BufferedReader;  
import java.io.BufferedWriter;  
import java.io.FileReader;  
import java.io.FileWriter;  
import java.io.IOException;  /**  * 按行处理文本文件(推荐实践)  * 适用于日志分析、配置文件解析等场景  */  
public class LineBasedExample {  public static void main(String[] args) {  String sourcePath = "access.log";  String targetPath = "clean.log";  try (BufferedReader reader = new BufferedReader(new FileReader(sourcePath));  BufferedWriter writer = new BufferedWriter(new FileWriter(targetPath))) {  String line;  // 逐行读取(自动过滤换行符,返回null表示文件结束)  while ((line = reader.readLine()) != null) {  // 处理逻辑:例如过滤空行或敏感信息  if (!line.trim().isEmpty()) {  writer.write(line);  writer.newLine();  // 写入平台适配的换行符(Windows为\r\n,Linux为\n)  }  }  System.out.println("按行处理文本完成!");  } catch (IOException e) {  e.printStackTrace();  }  }  
}  

 

四、实战案例:综合应用​

案例 1:统计文本文件单词出现次数​

需求:读取文本文件,统计每个单词的出现次数,忽略大小写和标点符号。

import java.io.BufferedReader;  
import java.io.FileReader;  
import java.io.IOException;  
import java.util.HashMap;  
import java.util.Map;  
import java.util.TreeMap;  public class WordCountTool {  public static void main(String[] args) {  String filePath = "input.txt";  Map<String, Integer> wordCounts = new HashMap<>();  try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {  String line;  while ((line = reader.readLine()) != null) {  // 预处理:转小写、去除首尾空格、分割单词  String[] words = line.trim().toLowerCase()  .split("[\\s\\p{Punct}]+");  // 按空格和标点分割  for (String word : words) {  if (!word.isEmpty()) {  wordCounts.merge(word, 1, Integer::sum);  // 统计次数  }  }  }  // 按单词字母顺序排序后输出  Map<String, Integer> sortedCounts = new TreeMap<>(wordCounts);  System.out.println("单词统计结果:");  sortedCounts.forEach((word, count) ->  System.out.printf("%-15s: %d%n", word, count)  );  } catch (IOException e) {  System.err.println("文件读取失败:" + e.getMessage());  }  }  
}  

案例 2:图片加密 / 解密(字节流应用)​

原理:使用异或运算(XOR)实现简单加密,相同密钥可加密和解密。

import java.io.BufferedInputStream;  
import java.io.BufferedOutputStream;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  public class ImageEncryptTool {  private static final byte ENCRYPT_KEY = 0x3B;  // 自定义加密密钥(1-255之间)  public static void main(String[] args) {  String sourcePath = "source.jpg";  String encryptedPath = "encrypted.jpg";  String decryptedPath = "decrypted.jpg";  try {  encryptFile(sourcePath, encryptedPath);  System.out.println("加密成功:" + encryptedPath);  encryptFile(encryptedPath, decryptedPath);  System.out.println("解密成功:" + decryptedPath);  } catch (IOException e) {  e.printStackTrace();  }  }  /**  * 异或加密/解密文件  * @param source 源文件路径  * @param target 目标文件路径  */  private static void encryptFile(String source, String target) throws IOException {  try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {  byte[] buffer = new byte[8192];  int bytesRead;  while ((bytesRead = bis.read(buffer)) != -1) {  for (int i = 0; i < bytesRead; i++) {  buffer[i] ^= ENCRYPT_KEY;  // 异或运算实现加密/解密  }  bos.write(buffer, 0, bytesRead);  }  }  }  
}  

 

五、Java I/O 流最佳实践​

1. 资源管理:强制使用try-with-resources

// 反例:手动关闭流(易遗漏,导致资源泄漏)  
FileInputStream fis = null;  
try {  fis = new FileInputStream("file.txt");  // 业务逻辑  
} finally {  if (fis != null) {  fis.close();  // 可能抛出IOException,需再次处理  }  
}  // 正例:自动关闭流(推荐,Java 7+)  
try (FileInputStream fis = new FileInputStream("file.txt")) {  // 业务逻辑(无需手动关闭,JVM自动处理)  
} catch (IOException e) {  e.printStackTrace();  
}  

2. 性能优化:永远使用缓冲流​

  • 字节流:优先使用BufferedInputStream/BufferedOutputStream,缓冲区大小建议 8KB(new byte[8192])。​
  • 字符流:优先使用BufferedReader/BufferedWriter,避免直接使用FileReader/FileWriter。​

3. 编码处理:显式指定字符集​

        文本文件读写时,始终通过InputStreamReader/OutputStreamWriter显式指定编码(如UTF-8),避免依赖系统默认编码导致乱码。​

4. 大文件处理:分段读取写入

// 大文件分段读取(例如1GB文件)  
long fileSize = new File("large.file").length();  
byte[] buffer = new byte[1024 * 1024];  // 1MB缓冲区  
while (fis.read(buffer) != -1 && totalRead < fileSize) {  fos.write(buffer, 0, (int) Math.min(buffer.length, fileSize - totalRead));  totalRead += buffer.length;  
}  

 

六、Java NIO:现代 I/O 解决方案

        NIO提供了更简洁、高效的文件操作 API,基于Path和Files类,支持异步 IO、文件属性访问等高级功能。​

示例:NIO快速复制文件

import java.io.IOException;  
import java.nio.file.Files;  
import java.nio.file.Path;  
import java.nio.file.Paths;  
import java.nio.file.StandardCopyOption;  public class NioFileCopy {  public static void main(String[] args) {  Path source = Paths.get("source.txt");  Path target = Paths.get("target.txt");  try {  // 快速复制文件(支持覆盖已存在文件)  Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);  System.out.println("NIO复制完成,性能优于传统I/O!");  // 读取所有行(适合小文件)  Files.lines(source).forEach(System.out::println);  } catch (IOException e) {  e.printStackTrace();  }  }  
}  

 

七、总结:选择合适的 I/O 方案​​

场景​

推荐 API​

核心优势​

二进制文件读写​

BufferedInputStream/BufferedOutputStream​

缓冲机制提升效率,支持任意二进制数据​

文本文件读写(小文件)​

BufferedReader/BufferedWriter​

按行处理,自动处理编码和换行符​

文本文件读写(大文件)​

NIO.2 Files.lines()/Files.write()​

流式处理,内存占用低,支持链式操作​

对象序列化​

ObjectInputStream/ObjectOutputStream​

支持 Java 对象的持久化存储​

        ​掌握 Java I/O 流的核心原理与使用技巧,是实现数据持久化、文件处理和网络通信的基础。建议开发者在实际项目中:​

  1. 根据数据类型(二进制 / 文本)选择字节流或字符流;​
  2. 始终使用缓冲流提升性能,避免直接操作基础流;​
  3. 显式处理字符编码,优先使用UTF-8保证跨平台兼容性;​
  4. 大文件处理时采用分段读写或 NIO.2 的高效 API。​

        对于高性能场景(如分布式文件系统、海量日志处理),可进一步学习 Java NIO 的通道(Channel)和缓冲区(Buffer)机制,以及异步 I/O 操作,提升系统吞吐量和响应速度。

http://www.xdnf.cn/news/9240.html

相关文章:

  • 夏季用电高峰如何防患于未“燃”?电力测温技术守护城市生命线
  • 使用 Redis 作为向量数据库
  • 5G 核心网 UE 状态深度剖析:机制、迁移与演进
  • 新版Chrome浏览器加载eDrawings 3D Viewer控件网页查看DWG、DXF
  • 利用Tushare+pyEcharts进行沪深证券数据采集与分析
  • 单向循环链表与双向链表
  • 洗鞋店干洗店线上预约管理系统;
  • 【OS安装与使用】part7-ubuntu22.04LTS 的 docker 安装与使用(实例:MTransServer服务部署)
  • AI辅助写作 从提笔难到高效创作的智能升级
  • WPF事件处理器+x名称空间
  • 基于多流特征融合与领域知识整合的CNN-xLSTM-xAtt网络用于光电容积脉搏波信号的无创血压估计【代码已复现】
  • C语言学习笔记三 --- V
  • 深信服防火墙拦截了DELETE、PUT请求,未达到nginx及后端服务
  • brep2seq 论文笔记
  • 《软件工程》第 16 章 - 软件项目管理与过程改进
  • C-字符串函数
  • 解析极限编程-拥抱变化(第2版)笔记
  • 【C/C++】多线程下自旋锁的行为逻辑
  • C语言创意编程:用趣味实例玩转基础语法(2)
  • ES 在大查询场景下导致 GC 频繁,如何定位和解决?
  • webstorm调试vite工程(后端开发人员版)
  • 从十进制到二进制:深入理解定点数与浮点数表示
  • 高压单端探头,如何实现大比例的衰减?
  • 【NLP基础知识系列课程-Tokenizer的前世今生第二课】NLP 中的 Tokenizer 技术发展史
  • 【Vue3】生命周期 hook函数 toRef
  • 通义智文开源QwenLong-L1: 迈向长上下文大推理模型的强化学习
  • 浅解Vue 数据可视化开发建议与速度优化
  • 【华为云物联网】如何实现在 MQTT.fx 上模拟数据间隔上传一次,并按设定系数变动数据
  • HTML 表单与输入:基础语法到核心应用全解析
  • UBUNTU20.04 配置以QT界面程序代替系统界面启动,以及如何在tty模式下以linuxfb形式启动