首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【关于Java的 I/O 流】

【关于Java的 I/O 流】

作者头像
艾伦耶格尔
发布2025-08-28 15:45:31
发布2025-08-28 15:45:31
9400
代码可运行
举报
文章被收录于专栏:Java基础Java基础
运行总次数:0
代码可运行

你有没有遇到过这样的问题?

👉 想读个配置文件,代码写了半天还是报错? 👉 上传个文件,内存爆了,程序直接卡死? 👉 面试官问:“说说 Java 的 I/O 流体系”,你脑子里一片混乱,只记得 FileInputStreamBufferedReader……然后就卡壳了 😣

带你彻底搞懂 Java 的 I/O 流(Input/Output Stream)


一、I/O 是啥?—— 程序的“进出口”

想象一下你家:

  • 水从水管进来 → 是 输入(Input)
  • 污水从下水道排出去 → 是 输出(Output)

在 Java 世界里,I/O 就是程序与外界“交换数据”的通道

数据来源可能是:

  • 文件(File)
  • 网络(Socket)
  • 内存(ByteArray)
  • 控制台(键盘/屏幕)

而“流(Stream)”就像一条单向的管道,数据从一端流向另一端。

输入流(InputStream / Reader):数据 → 程序(比如读文件) ✅ 输出流(OutputStream / Writer):程序 → 数据(比如写文件)


二、字节流 vs 字符流 —— 二进制 vs 文本

Java 的 I/O 分两大派:

1. 字节流(Byte Stream)—— 处理“原始二进制”

适用于:图片、视频、音频、PDF、任何二进制文件。

核心类:

  • InputStream(输入)
  • OutputStream(输出)

常见实现:

  • FileInputStream / FileOutputStream:操作文件
  • ByteArrayInputStream / ByteArrayOutputStream:操作内存数组
  • ObjectInputStream / ObjectOutputStream:对象序列化
代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
复制
// 拷贝图片文件(字节流)
FileInputStream in = new FileInputStream("src.jpg");
FileOutputStream out = new FileOutputStream("dest.jpg");

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
    out.write(buffer, 0, len);
}
in.close();
out.close();

2. 字符流(Character Stream)—— 处理“文本”,自动处理编码

适用于:文本文件、JSON、XML、配置文件。

核心类:

  • Reader(输入)
  • Writer(输出)

常见实现:

  • FileReader / FileWriter:简单读写文本(不推荐直接用)
  • BufferedReader / BufferedWriter:带缓冲,性能好(推荐!)
  • InputStreamReader / OutputStreamWriter:桥接字节流和字符流,可指定编码
代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
复制
// 读取文本文件(字符流 + 缓冲)
BufferedReader reader = new BufferedReader(
    new InputStreamReader(
        new FileInputStream("config.txt"), "UTF-8"
    )
);

String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

💡 为什么推荐 BufferedReader 因为它自带“缓冲区”,不是每次 read() 都去磁盘读,而是先读一大块存内存里,再慢慢取,效率高!


三、缓冲流:给 I/O 装个“加速器”

直接读写文件 = 每次都去“跑一趟银行” 加上缓冲流 = 先取一万元现金放家里,要用就从家里拿,省时间!

代码语言:javascript
代码运行次数:0
运行
复制
// 不带缓冲:慢!
FileInputStream in = new FileInputStream("big.log");
// 每次 read() 都可能触发一次磁盘 I/O

// 带缓冲:快!
BufferedInputStream bufferedIn = new BufferedInputStream(
    new FileInputStream("big.log")
);
// 一次性读 8KB 到内存,后续 read() 从内存取

最佳实践: 无论字节流还是字符流,只要涉及文件读写,都加上 Buffered 包装


四、对象流:直接读写“Java 对象”

想把一个 User 对象保存到文件?用 ObjectOutputStream

代码语言:javascript
代码运行次数:0
运行
复制
// 序列化:对象 → 文件
User user = new User("张三", 25);
ObjectOutputStream oos = new ObjectOutputStream(
    new FileOutputStream("user.obj")
);
oos.writeObject(user);
oos.close();

// 反序列化:文件 → 对象
ObjectInputStream ois = new ObjectInputStream(
    new FileInputStream("user.obj")
);
User savedUser = (User) ois.readObject();
ois.close();

⚠️ 注意:对象必须实现 Serializable 接口!


五、NIO 简要介绍:I/O 的“高性能版本”

传统 I/O(BIO)是阻塞式的: “你读文件,程序就等着,啥也不能干。”

NIO(New I/O)是非阻塞 + 事件驱动的,适合高并发场景(如 Netty、Redis)。

核心概念:

  • Channel:双向通道(比流更强大)
  • Buffer:数据缓冲区
  • Selector:监听多个通道的事件

💡 面试如果问到,可以说: “我们项目用的是传统 I/O 处理文件上传下载。高并发网络通信用 Netty,底层是 NIO,性能更好。”


六、常见坑 & 最佳实践

❌ 坑1:忘记关闭流 → 内存泄漏、文件锁
代码语言:javascript
代码运行次数:0
运行
复制
// 错误写法
FileInputStream in = new FileInputStream("a.txt");
// 忘了 close(),文件一直被占用!

✅ 正确做法:用 try-with-resources

代码语言:javascript
代码运行次数:0
运行
复制
try (FileInputStream in = new FileInputStream("a.txt");
     FileOutputStream out = new FileOutputStream("b.txt")) {

    byte[] buffer = new byte[1024];
    int len;
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0, len);
    }
    // 自动关闭!无需 finally
}
❌ 坑2:编码问题 → 乱码!
代码语言:javascript
代码运行次数:0
运行
复制
// 错误:用默认编码(可能是 GBK),文件是 UTF-8 就乱码
FileReader reader = new FileReader("data.txt");

// 正确:显式指定 UTF-8
BufferedReader br = new BufferedReader(
    new InputStreamReader(
        new FileInputStream("data.txt"), "UTF-8"
    )
);
✅ 最佳实践总结:
  1. 读文本用 BufferedReader + InputStreamReader(指定编码)
  2. 写文本用 BufferedWriter + OutputStreamWriter
  3. 读写二进制用 BufferedInputStream / BufferedOutputStream
  4. 必须用 try-with-resources 自动关闭流
  5. 涉及中文,显式指定 UTF-8 编码

七、高频问题 & 高分回答

Q1: 字节流和字符流有什么区别?

  • 字节流:以 byte 为单位,适合处理二进制数据(图片、视频),基类是 InputStream/OutputStream
  • 字符流:以 char 为单位,自动处理字符编码,适合处理文本,基类是 Reader/Writer。 实际开发中,读写文件都会加上 Buffered 提升性能。

Q2: 为什么要用缓冲流?

: 因为直接读写磁盘或网络 I/O 非常慢。缓冲流在内存中开辟一块缓冲区,一次性读取较多数据,后续操作从内存取,大大减少 I/O 次数,提升性能。 我们项目处理大文件时,必须用 BufferedInputStream,否则速度极慢。


Q3: 如何保证流正确关闭?

: 使用 try-with-resources 语法,它能自动调用 close() 方法,即使发生异常也能保证资源释放。 这是 Java 7 引入的特性,比传统的 try-catch-finally 更简洁、更安全。


Q4: 说说你项目中 I/O 的使用场景?

  1. 配置文件读取:用 BufferedReader 读取 application.properties
  2. 文件上传下载:Spring MVC 用 InputStream 接收上传文件,OutputStream 返回下载流;
  3. 日志记录:Logback 底层用 FileOutputStream 写日志文件;
  4. 对象持久化:用 ObjectOutputStream 临时保存缓存对象。 所有 I/O 操作都加了缓冲,并用 try-with-resources 管理资源。

✅ 总结:一张表搞懂 I/O 流怎么选

需求

推荐类

读文本文件(UTF-8)

BufferedReader + InputStreamReader

写文本文件

BufferedWriter + OutputStreamWriter

读二进制文件(图片)

BufferedInputStream + FileInputStream

写二进制文件

BufferedOutputStream + FileOutputStream

读写对象

ObjectInputStream / ObjectOutputStream

自动关闭资源

try-with-resources


🔚 最后一句话

I/O 流不是“古董”,而是“基础设施”。 无论你是做 Web、后端、大数据,都绕不开文件读写。 掌握它,你才能写出高效、安全、不乱码的程序!

希望这篇能帮你彻底搞懂 Java I/O 流!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、I/O 是啥?—— 程序的“进出口”
  • 二、字节流 vs 字符流 —— 二进制 vs 文本
    • 1. 字节流(Byte Stream)—— 处理“原始二进制”
    • 2. 字符流(Character Stream)—— 处理“文本”,自动处理编码
  • 三、缓冲流:给 I/O 装个“加速器”
  • 四、对象流:直接读写“Java 对象”
  • 五、NIO 简要介绍:I/O 的“高性能版本”
  • 六、常见坑 & 最佳实践
    • ❌ 坑1:忘记关闭流 → 内存泄漏、文件锁
    • ❌ 坑2:编码问题 → 乱码!
    • ✅ 最佳实践总结:
  • 七、高频问题 & 高分回答
    • Q1: 字节流和字符流有什么区别?
    • Q2: 为什么要用缓冲流?
    • Q3: 如何保证流正确关闭?
    • Q4: 说说你项目中 I/O 的使用场景?
  • ✅ 总结:一张表搞懂 I/O 流怎么选
  • 🔚 最后一句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档