Java IO 从入门到深入(第二篇):字节流 InputStream / OutputStream 详解
在上一篇文章中,我们介绍了:
- Java IO 整体体系
- File 类的使用
但 File 只能 表示文件路径,并不能真正读取或写入文件内容。
如果想操作文件数据,就必须使用 IO流(Stream)。
本篇我们将进入 Java IO 最核心的一部分:
字节流(Byte Stream)
本文主要内容:
- 什么是字节流
- InputStream / OutputStream 体系结构
- 常用字节流类
- 文件读取示例
- 文件写入示例
- 文件复制案例(高频)
- try-with-resources(现代写法)
- 常见易错点
- 面试高频问题
一、什么是字节流?
字节流是 Java IO 中最基础的一种流。
特点:
以字节(byte)为单位进行数据读写
适用于:
图片
视频
音频
压缩包
二进制文件
例如:
jpg
png
mp4
zip
exe
这些文件本质上都是 二进制数据。
因此 Java 使用 字节流处理它们。
二、Java 字节流的核心类
Java 字节流分为两大类:
InputStream
OutputStream
结构:
字节流
│
├── InputStream
│ 文件读取
│
└── OutputStream
文件写入
简单理解:
| 类 | 作用 |
|---|---|
| InputStream | 读取数据 |
| OutputStream | 写入数据 |
三、InputStream 体系结构
InputStream 是所有字节输入流的 抽象父类。
常见子类:
InputStream
│
├── FileInputStream
├── BufferedInputStream
├── ByteArrayInputStream
└── ObjectInputStream
其中最常用的是:
FileInputStream
用于 读取文件数据。
四、OutputStream 体系结构
OutputStream 是所有字节输出流的 抽象父类。
结构:
OutputStream
│
├── FileOutputStream
├── BufferedOutputStream
├── ByteArrayOutputStream
└── ObjectOutputStream
最常用:
FileOutputStream
用于 写入文件数据。
五、InputStream 常用方法
read()
java
int read()
作用:
读取一个字节
返回值:
| 返回值 | 含义 |
|---|---|
| 0~255 | 读取到的字节 |
| -1 | 文件结束 |
示例:
java
FileInputStream fis = new FileInputStream("test.txt");
int data;
while((data = fis.read()) != -1){
System.out.print((char)data);
}
fis.close();
六、批量读取(推荐)
逐字节读取效率较低。
推荐:
java
read(byte[])
示例:
java
FileInputStream fis = new FileInputStream("test.txt");
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
String str = new String(buffer,0,len);
System.out.print(str);
}
fis.close();
解释:
缓冲区 (Buffer):byte[1024]就是一个临时的"数据容器",一次读取多个字节,减少磁盘访问次数,提升效率。
实际长度 (len):fis.read(buffer)的返回值。它表示本次实际读入了多少字节。文件末尾时返回 -1。这个值至关重要,因为缓冲区最后一块可能没被填满。
循环处理:只要没读到文件尾 (len != -1),就进入循环。
正确转换:new String(buffer, 0, len)用 len确保只将本次读入的有效字节转换成字符串,避免处理缓冲区末尾的"旧数据"。
一句话流程:用一个缓冲区反复批量读取文件,每次只处理实际读到的数据,读完为止。
流程模拟(读取一个1500字节的文件)
| 循环次数 | buffer状态 (大小1024) |
len的值 (实际读取字节数) |
转换字符串的参数 | 说明 |
|---|---|---|---|---|
| 第1次 | 被文件前1024字节填满 | 1024 | new String(buffer, 0, 1024) |
读取并处理了前1024字节。 |
| 第2次 | 被文件剩下476字节 覆盖。 前476字节是新数据,后面548字节是上次的旧数据。 | 476 | new String(buffer, 0, 476) |
只处理新的476字节,忽略缓冲区后面的旧数据。 |
| 第3次 | 无新数据,read返回-1 |
-1 | 不进入循环 | 文件结束,循环退出。 |
七、OutputStream 常用方法
write(int b)
写入一个字节:
java
fos.write(65);
写入字符:
A
write(byte[])
写入字节数组:
java
String str = "Hello Java";
fos.write(str.getBytes());
八、写入文件示例
完整示例:
java
import java.io.FileOutputStream;
public class Main {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("test.txt");
String text = "Hello Java IO";
fos.write(text.getBytes());
fos.close();
}
}
执行后:
test.txt
内容:
Hello Java IO
九、文件追加写入
默认:
覆盖文件
如果需要 追加写入:
java
FileOutputStream fos = new FileOutputStream("test.txt", true);
示例:
java
fos.write("Hello".getBytes());
十、经典案例:文件复制(面试高频)
文件复制是 Java IO 面试经典题。
示例:
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class CopyFile {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.jpg");
FileOutputStream fos = new FileOutputStream("b.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
fis.close();
fos.close();
}
}
核心逻辑:
读 → 写
读 → 写
读 → 写
直到:
read() = -1
十一、try-with-resources(推荐写法)
传统写法:
需要手动 close()
现代 Java 推荐:
try-with-resources
示例:
java
try(FileInputStream fis = new FileInputStream("a.txt")){
int data;
while((data = fis.read()) != -1){
System.out.print((char)data);
}
}
优势:
自动关闭资源
代码更安全
十二、字节流的使用场景
字节流适合:
1 文件复制
例如:
图片
视频
压缩包
2 文件下载
Web系统下载:
浏览器 ← 服务器
3 上传文件
例如:
MultipartFile
底层也是字节流。
十三、常见易错点
1 忘记关闭流
错误:
资源泄露
推荐:
try-with-resources
2 read() 返回值
很多人写:
java
while(fis.read() != -1)
但没有保存返回值。
正确:
java
int data;
while((data = fis.read()) != -1)
3 写入 buffer 错误
错误写法:
java
fos.write(buffer);
正确:
java
fos.write(buffer,0,len);
否则会写入 脏数据。
十四、面试高频问题
1 InputStream 和 Reader 的区别
| 类型 | 单位 |
|---|---|
| InputStream | byte |
| Reader | char |
2 为什么 read() 返回 int?
原因:
byte = -128 ~ 127
而一个字节范围是0 ------ 255,用bute注定无法使用-1为结束
但:
-1 表示结束
因此必须使用 int。
3 Java IO 如何提高性能?
方法:
缓冲流
BufferedInputStream
BufferedOutputStream
4 Java IO 如何避免资源泄露?
推荐:
try-with-resources
十五、总结
本篇介绍了 Java IO 字节流。
核心类:
InputStream
OutputStream
最常用实现:
FileInputStream
FileOutputStream
核心方法:
read()
read(byte[])
write()
write(byte[])
经典应用:
文件读取
文件写入
文件复制