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

深入理解 Java IO 流 —— 从入门到实战

        在 Java 开发中,IO 流(Input/Output Stream) 是最常用、最基础的技术之一。无论是文件操作、网络传输,还是日志记录与数据持久化,都离不开 IO 流的支持。本文将带你系统学习 Java IO 流,涵盖 字节流、字符流、缓冲流、转换流、对象流、标准流、打印流 等内容,并结合大量案例,帮助你在实际开发中灵活应用。


一、IO 流的基本概念

在计算机系统中,所有的数据都是以 二进制(0 和 1) 的形式存储的。IO 流是一种抽象的概念,用于描述数据在 程序与设备(文件、内存、网络、控制台等)之间的传输方式

1.1 流的方向

  • 输入流(Input Stream):数据从设备流入程序,例如从文件读取数据。

  • 输出流(Output Stream):数据从程序流向设备,例如向文件写入数据。

记忆方法:始终以 程序为参照物,从程序角度判断方向。

1.2 按数据单位分类

  • 字节流(byte):以字节为单位(1 byte = 8 bit),适合处理任意文件(文本、图片、视频、音频等)。

  • 字符流(char):以字符为单位,专门用于处理文本文件,支持字符集编码。

1.3 四大抽象基类

在 Java 中,所有 IO 流都派生自以下四个抽象基类:

  • InputStream —— 字节输入流的父类

  • OutputStream —— 字节输出流的父类

  • Reader —— 字符输入流的父类

  • Writer —— 字符输出流的父类

所有具体流类都继承自它们之一。


二、字节流详解

2.1 FileInputStream —— 文件字节输入流

用于从文件中读取字节数据。

InputStream is = new FileInputStream("D:/test/a.txt");
int data;
while ((data = is.read()) != -1) {System.out.print((char) data);
}
is.close();

常用方法:

  • int read():读取单个字节,返回值为字节数据或 -1(文件结束)。

  • int read(byte[] b):读取多个字节到数组,返回实际读取长度。

  • int read(byte[] b, int off, int len):读取部分字节到数组指定位置。

2.2 FileOutputStream —— 文件字节输出流

用于向文件中写入字节数据。

OutputStream os = new FileOutputStream("D:/test/b.txt");
os.write("Hello IO".getBytes());
os.close();

常用方法:

  • void write(int b):写入单个字节。

  • void write(byte[] b):写入整个字节数组。

  • void write(byte[] b, int off, int len):写入数组部分内容。

⚠ 注意:默认会覆盖原文件内容,可以使用 追加模式

OutputStream os = new FileOutputStream("D:/test/b.txt", true);

2.3 案例:文件复制

InputStream is = new FileInputStream("src/a.txt");
OutputStream os = new FileOutputStream("src/b.txt");byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {os.write(buffer, 0, len);
}os.close();
is.close();

三、字符流详解

字符流适用于文本文件,支持编码和解码。

3.1 FileReader —— 文件字符输入流

Reader reader = new FileReader("src/a.txt");
int ch;
while ((ch = reader.read()) != -1) {System.out.print((char) ch);
}
reader.close();

3.2 FileWriter —— 文件字符输出流

Writer writer = new FileWriter("src/b.txt");
writer.write("你好,世界!\n");
writer.close();

⚠ 注意:字符流不能操作非文本文件(如图片、视频),否则会乱码或损坏文件。

3.3 案例:文本文件拷贝

Reader reader = new FileReader("src/a.txt");
Writer writer = new FileWriter("src/b.txt", true); // 追加模式char[] buffer = new char[1024];
int len;
while ((len = reader.read(buffer)) != -1) {writer.write(buffer, 0, len);
}writer.close();
reader.close();

四、缓冲流

缓冲流是一种 增强流,通过 缓冲区机制 提升读写效率。

4.1 缓冲字节流

  • BufferedInputStream

  • BufferedOutputStream

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/a.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/b.jpg"));byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {bos.write(buffer, 0, len);
}bos.close();
bis.close();

4.2 缓冲字符流

  • BufferedReader —— 新增 readLine() 方法,可逐行读取。

  • BufferedWriter —— 新增 newLine() 方法,跨平台换行。

BufferedReader br = new BufferedReader(new FileReader("src/a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("src/b.txt"));String line;
while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();
}bw.close();
br.close();

五、转换流

5.1 InputStreamReader 与 OutputStreamWriter

  • 作用:字节流和字符流之间的桥梁。

  • 解决编码问题(UTF-8 / GBK)。

Reader reader = new InputStreamReader(new FileInputStream("src/gbk.txt"), "GBK");
int ch;
while ((ch = reader.read()) != -1) {System.out.print((char) ch);
}
reader.close();
Writer writer = new OutputStreamWriter(new FileOutputStream("src/utf8.txt"), "UTF-8");
writer.write("你好,世界!");
writer.close();

六、标准流

  • System.in —— 标准输入(键盘)。

  • System.out —— 标准输出(控制台)。

  • System.err —— 标准错误输出。

案例:重定向标准输出

PrintStream ps = new PrintStream("log.txt");
System.setOut(ps);
System.out.println("日志内容");

七、对象流

用于对象序列化与反序列化。

7.1 ObjectOutputStream

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.obj"));
oos.writeObject(new Person("张三", 20));
oos.close();

7.2 ObjectInputStream

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.obj"));
Person p = (Person) ois.readObject();
ois.close();

⚠ 注意:

  • 对象必须实现 Serializable 接口。

  • transient 修饰的属性不会被序列化。


八、打印流

8.1 PrintStream

PrintStream ps = new PrintStream("out.txt");
ps.println("Hello PrintStream");
ps.printf("姓名: %s, 年龄: %d", "张三", 20);
ps.close();

8.2 PrintWriter

支持字符编码设置,常用于 Web 开发。

PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8"));
pw.println("你好,世界");
pw.close();

九、实战应用场景

  1. 文件拷贝工具类:封装字节流操作,支持大文件复制。

  2. 日志系统:使用 PrintWriter 结合 BufferedWriter 实现高效日志写入。

  3. 配置文件读取:使用 BufferedReader 逐行解析配置。

  4. 对象持久化:使用 ObjectOutputStream 将对象保存到磁盘。


十、总结与面试考点

在学习和掌握了 IO 流的概念、分类以及常用的 API 之后,我们来总结一下核心要点,并结合常见的面试问题进行详细分析。

10.1 字节流与字符流的区别

  • 字节流:以字节为单位(8 位),几乎可以操作所有类型的文件,包括文本、图片、音频、视频。

  • 字符流:以字符为单位(16 位),只适合处理文本数据,更加关注字符编码(UTF-8、GBK 等)。

  • 面试陷阱:为什么图片或音频必须用字节流?因为它们不是文本文件,字符流会尝试编码/解码,导致文件损坏。

10.2 输入流与输出流的区别

  • 输入流:从外部数据源(文件、网络、内存)读取数据到程序中。

  • 输出流:将数据从程序写出到外部目标(文件、网络、内存)。

  • 面试常问:如果要实现文件复制,至少需要哪两种流?答案是输入流和输出流配合使用。

10.3 节点流与处理流(增强流)

  • 节点流:直接与数据源或目的地交互,例如 FileInputStream、FileReader。

  • 处理流(增强流):对节点流进行包装,增加功能或提高性能,例如 BufferedInputStream、InputStreamReader。

  • 面试考点:为什么要使用缓冲流?答:减少磁盘 IO 次数,提升效率。

10.4 常见流的应用场景

  • 字节流:复制二进制文件(图片、视频、音频)。

  • 字符流:读写文本文件。

  • 缓冲流:高效读写大文件,适合日志系统。

  • 转换流:解决编码问题(GBK ↔ UTF-8)。

  • 对象流:实现对象序列化与反序列化。

  • 打印流:日志打印、格式化输出。

10.5 异常处理与资源释放

  • 传统方式:try-catch-finally 手动关闭流。

  • JDK 7+:try-with-resources 自动关闭流,更加简洁安全。

  • 面试延伸:为什么必须关闭流?答:流会占用操作系统资源(文件句柄、内存等),不关闭会造成资源泄露。

10.6 编码问题与解决方案

  • 不同系统的默认编码可能不同,可能导致乱码。

  • 使用转换流(InputStreamReader/OutputStreamWriter)指定编码格式,避免乱码。

  • 面试延伸:为什么 Windows 下用 FileReader 读取 UTF-8 文件可能乱码?答:因为 FileReader 使用系统默认编码(Windows 默认 GBK),与文件实际编码(UTF-8)不一致。

10.7 对象序列化与反序列化

  • 序列化:将对象转为字节流,写入文件或通过网络传输。

  • 反序列化:将字节流还原为对象。

  • 必须实现 Serializable 接口。

  • 关键字transient 修饰的变量不会被序列化。

  • 面试考点:如果对象中有不可序列化的成员怎么办?答:使用 transient 忽略,或让其也实现 Serializable。

10.8 高级面试题示例

  1. 问题:如何在 Java 中实现文件的高效复制? 答案:使用字节缓冲流(BufferedInputStream/BufferedOutputStream),避免逐字节操作。

  2. 问题:如何避免 IO 流中出现内存泄漏? 答案:始终关闭流,推荐使用 JDK7+ 的 try-with-resources。

  3. 问题:字节流和字符流能否互换? 答案:不能直接互换,但可以通过转换流(InputStreamReader、OutputStreamWriter)实现。

  4. 问题:为什么 PrintWriter 在 Web 开发中更常用? 答案:因为 PrintWriter 支持字符编码设置,更适合输出文本数据(如 HTML、JSON)。


结语

Java IO 流体系庞大而灵活,是 Java 基础中的重点。掌握 IO 流不仅能应对常见的文件读写,还能为网络编程、多线程日志、持久化存储等提供坚实基础。希望本文的系统总结与案例讲解,能帮助你在学习与开发中得心应手,也能在面试中脱颖而出。

http://www.dtcms.com/a/346269.html

相关文章:

  • 力扣(在排序数组中查找元素的第一个和最后一个位置)
  • Codeforces 更换
  • 零知开源——基于ESP8266(ESP-12F)驱动YS-IR05F红外控制空调
  • SRE系列(二) | 从可用性到 SLI/SLO
  • nginx-限速-限制并发连接数-限制请求数
  • 洛谷 P3811 【模板】模意义下的乘法逆元-普及/提高-
  • html基本元素
  • 嵌入式第三十五天(网络编程(UDP))
  • 特大桥施工绳断 7 人亡:索力实时监测预警机制亟待完善
  • STM32F1 EXTI介绍及应用
  • tiktok滑块反爬分析verifyV2
  • Linux设备模型技术路线图
  • B树,B+树,B*树
  • Codeforces Round 1043 (Div. 3)
  • set_case_analysis应用举例
  • 技术里常说 没有银弹
  • 纳米软件自动化测试平台ATECLOUD产品手册之一——系统介绍
  • 声网如何让AI理解画面、情绪和你说的话
  • 【资源分享】(影视相关)
  • Claude Code 三类.md文件
  • Java 18 新特性及具体应用
  • WMS选型攻略:钱该省在哪?部署怎么定?
  • openEuler系统安装Ascend Docker Runtime的方法
  • open webui源码分析7—过滤器
  • 劳务工队:建筑工程的基石力量,行业生态的多元拼图
  • RKLLM 模型转换从0开始
  • 测试工程师面试题 + 简短答案
  • Scala面试题及详细答案100道(1-10)-- 基础语法与数据类型
  • 如何理解AP服务发现协议中“如果某项服务需要被配置为可通过多个不同的网络接口进行访问,则应为每个网络接口使用一个独立的客户端服务实例”?
  • 异步开发相关概念