你有没有遇到过这样的问题?
👉 想读个配置文件,代码写了半天还是报错?
👉 上传个文件,内存爆了,程序直接卡死?
👉 面试官问:“说说 Java 的 I/O 流体系”,你脑子里一片混乱,只记得 FileInputStream
和 BufferedReader
……然后就卡壳了 😣
带你彻底搞懂 Java 的 I/O 流(Input/Output Stream)!
想象一下你家:
在 Java 世界里,I/O 就是程序与外界“交换数据”的通道。
数据来源可能是:
而“流(Stream)”就像一条单向的管道,数据从一端流向另一端。
✅ 输入流(InputStream / Reader):数据 → 程序(比如读文件) ✅ 输出流(OutputStream / Writer):程序 → 数据(比如写文件)
Java 的 I/O 分两大派:
适用于:图片、视频、音频、PDF、任何二进制文件。
核心类:
InputStream
(输入)OutputStream
(输出)常见实现:
FileInputStream
/ FileOutputStream
:操作文件ByteArrayInputStream
/ ByteArrayOutputStream
:操作内存数组ObjectInputStream
/ ObjectOutputStream
:对象序列化
// 拷贝图片文件(字节流)
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();
适用于:文本文件、JSON、XML、配置文件。
核心类:
Reader
(输入)Writer
(输出)常见实现:
FileReader
/ FileWriter
:简单读写文本(不推荐直接用)BufferedReader
/ BufferedWriter
:带缓冲,性能好(推荐!)InputStreamReader
/ OutputStreamWriter
:桥接字节流和字符流,可指定编码
// 读取文本文件(字符流 + 缓冲)
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()
都去磁盘读,而是先读一大块存内存里,再慢慢取,效率高!
直接读写文件 = 每次都去“跑一趟银行” 加上缓冲流 = 先取一万元现金放家里,要用就从家里拿,省时间!
// 不带缓冲:慢!
FileInputStream in = new FileInputStream("big.log");
// 每次 read() 都可能触发一次磁盘 I/O
// 带缓冲:快!
BufferedInputStream bufferedIn = new BufferedInputStream(
new FileInputStream("big.log")
);
// 一次性读 8KB 到内存,后续 read() 从内存取
✅ 最佳实践: 无论字节流还是字符流,只要涉及文件读写,都加上
Buffered
包装!
想把一个 User
对象保存到文件?用 ObjectOutputStream
!
// 序列化:对象 → 文件
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
接口!
传统 I/O(BIO)是阻塞式的: “你读文件,程序就等着,啥也不能干。”
NIO(New I/O)是非阻塞 + 事件驱动的,适合高并发场景(如 Netty、Redis)。
核心概念:
Channel
:双向通道(比流更强大)Buffer
:数据缓冲区Selector
:监听多个通道的事件💡 面试如果问到,可以说: “我们项目用的是传统 I/O 处理文件上传下载。高并发网络通信用 Netty,底层是 NIO,性能更好。”
// 错误写法
FileInputStream in = new FileInputStream("a.txt");
// 忘了 close(),文件一直被占用!
✅ 正确做法:用 try-with-resources
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
}
// 错误:用默认编码(可能是 GBK),文件是 UTF-8 就乱码
FileReader reader = new FileReader("data.txt");
// 正确:显式指定 UTF-8
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"), "UTF-8"
)
);
BufferedReader
+ InputStreamReader
(指定编码)BufferedWriter
+ OutputStreamWriter
BufferedInputStream
/ BufferedOutputStream
答:
byte
为单位,适合处理二进制数据(图片、视频),基类是 InputStream
/OutputStream
;char
为单位,自动处理字符编码,适合处理文本,基类是 Reader
/Writer
。
实际开发中,读写文件都会加上 Buffered
提升性能。答: 因为直接读写磁盘或网络 I/O 非常慢。缓冲流在内存中开辟一块缓冲区,一次性读取较多数据,后续操作从内存取,大大减少 I/O 次数,提升性能。 我们项目处理大文件时,必须用
BufferedInputStream
,否则速度极慢。
答: 使用 try-with-resources 语法,它能自动调用
close()
方法,即使发生异常也能保证资源释放。 这是 Java 7 引入的特性,比传统的try-catch-finally
更简洁、更安全。
答:
BufferedReader
读取 application.properties
;InputStream
接收上传文件,OutputStream
返回下载流;FileOutputStream
写日志文件;ObjectOutputStream
临时保存缓存对象。
所有 I/O 操作都加了缓冲,并用 try-with-resources 管理资源。需求 | 推荐类 |
---|---|
读文本文件(UTF-8) | BufferedReader + InputStreamReader |
写文本文件 | BufferedWriter + OutputStreamWriter |
读二进制文件(图片) | BufferedInputStream + FileInputStream |
写二进制文件 | BufferedOutputStream + FileOutputStream |
读写对象 | ObjectInputStream / ObjectOutputStream |
自动关闭资源 | try-with-resources |
I/O 流不是“古董”,而是“基础设施”。 无论你是做 Web、后端、大数据,都绕不开文件读写。 掌握它,你才能写出高效、安全、不乱码的程序!
希望这篇能帮你彻底搞懂 Java I/O 流!