JavaEE初阶-Java I/O:字节流与字符流的本质区别

1.InputStream 和 Reader 之间的区别
这是 Java I/O 中最基础也是最重要的区别之一。简单来说:
-
InputStream(字节流):处理原始的字节 (byte) 数据。 -
Reader(字符流):处理文本字符 (char) 数据。
Reader 知道如何将 InputStream 中的字节解码为字符。
(1) 核心区别:字节 vs 字符
-
字节 (Byte):
-
InputStream的read()方法一次读取 1 个字节 (8-bit)。 -
它完全不关心这些字节代表什么。一个英文字母 'A' 是 1 字节,一个汉字 '你' 在 UTF-8 编码下可能是 3 字节。
-
用途:适用于所有类型的文件,特别是二进制文件,如图片 (
.jpg), 视频 (.mp4), 可执行文件 (.exe)。
-
-
字符 (Character):
-
Reader的read()方法一次读取 1 个字符 (Java 内部使用 16-bitchar)。 -
它内部包含了一个解码器。它会从底层的字节流中读取一个或多个字节(例如,在 UTF-8 中读取 3 字节),然后将它们“组装”成一个完整的字符 '你'。
-
用途:专用于处理文本数据 (
.txt,.java,.xml,.json)。
-
(2) 桥梁:InputStreamReader
InputStream 和 Reader 不是非此即彼的,它们是协同工作的。
InputStreamReader就是一座“桥梁”。它包装一个InputStream(数据源,提供字节),并将其转换为一个Reader(数据视图,提供字符)。
在创建 InputStreamReader 时,你必须(或者说“强烈建议”)指定字符编码 (Charset),如 "UTF-8"。
Java
// 逐行注释:1. 获取一个原始的字节流(例如,从文件)。
FileInputStream fis = new FileInputStream("myFile.txt");
// 逐行注释:2. 创建桥梁:InputStreamReader。
// 逐行注释:它包装了字节流 fis,并告诉它使用 "UTF-8" 编码来解码字节。
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
// 逐行注释:3. 现在你可以从 reader 中愉快地读取“字符”了,不用担心字节错乱。
int charData = reader.read(); // 这里读到的是一个完整的 char,可能是 'A' 或 '你'
(3) 详细对比表
| 特性 (Feature) | InputStream (字节流) | Reader (字符流) |
| 处理单元 | 字节 (Byte) | 字符 (Character) |
基本 read() 方法 |
int read() (返回 0-255 的字节值, 或 -1) |
int read() (返回 0-65535 的字符值, 或 -1) |
| 主要用途 | 任何原始数据(图片、音频、二进制) | 纯文本数据(.txt, .xml, .java) |
| 是否处理编码 | 否。它只认字节。 | 是。它使用指定的字符集(Charset)解码。 |
| 常见子类 | FileInputStream, ByteArrayInputStream |
FileReader, StringReader, InputStreamReader |
| 对应的输出流 | OutputStream (字节输出流) |
Writer (字符输出流) |
(4) 为什么这个区别很重要?
如果你错误地使用了 InputStream 来读取文本文件,尤其是包含非英文字符(如中文、日文、表情符号)的文本:
-
你
read()到的将是组成这些字符的零散字节。 -
例如,读取 "你好" (UTF-8 编码下占 6 字节),你用
InputStream会分 6 次读到 6 个字节。你无法直接将它们拼成 "你好"。 -
而使用
Reader(指定 UTF-8),你调用read()两次,就会分别得到字符 '你' 和 '好'。
结论:
-
要复制文件、上传/下载文件、处理图片/视频/音频:用
InputStream。 -
要读取配置文件、解析 JSON/XML、处理用户输入的文本:用
Reader。
接下来,您是想了解 OutputStream 和 Writer 之间的区别(这与 InputStream 和 Reader 正好对应)
这是一个绝佳的问题,它触及了 Java I/O 体系的另外两个核心概念,以及新旧 I/O 模型的演进。
我们来逐一分解。
2. 📤 OutputStream 和 Writer 之间的区别
这个区别与 InputStream 和 Reader 的区别是完全对应的。一个是写字节,一个是写字符。
-
OutputStream(字节输出流):用于写入原始的字节 (byte)。 -
Writer(字符输出流):用于写入文本字符 (char)。
Writer 知道如何将 Java 内部的字符编码 (Encode) 为字节,然后交给底层的 OutputStream 去写入。
(1) 核心区别:编码 (Encoding)
-
OutputStream(字节流)-
它的
write()方法接受int(只写低8位)、byte[]。 -
它不关心你给它的字节是什么含义。它只是一个“字节管道”。
-
如果你想用它来写文本(比如 "你好"),你必须手动将字符串编码为字节数组,并处理
UnsupportedEncodingException。 -
用途:写入二进制数据,如保存图片、写入
.zip文件、网络套接字传输原始数据。
-
-
Writer(字符流)-
它的
write()方法接受int(char)、char[],甚至直接接受String。 -
它内部包含了一个编码器。你给它一个字符 '你',它会自动根据你指定的(或默认的)字符集(如 UTF-8)将其转换为 3 个字节,然后写入底层的输出流。
-
用途:专用于写入文本数据(生成
.txt,.html,.json文件等)。
-
(2) 桥梁:OutputStreamWriter
与 InputStreamReader 对应,OutputStreamWriter 是连接字符世界和字节世界的“桥梁”。
OutputStreamWriter包装一个OutputStream(字节的“目的地”),并将其转换为一个Writer(字符的“接收口”)。
创建时指定编码至关重要,它决定了你的字符如何被转换成字节。
Java
// 逐行注释:1. 获取一个原始的字节输出流(例如,写入文件)。
FileOutputStream fos = new FileOutputStream("output.txt");
// 逐行注释:2. 创建桥梁:OutputStreamWriter。
// 逐行注释:它包装了字节流 fos,并告诉它使用 "UTF-8" 编码来“转换”字符。
Writer writer = new OutputStreamWriter(fos, "UTF-8");
// 逐行注释:3. 现在你可以直接写入“字符串”了。
writer.write("你好, Java!"); // Writer 会自动将 "你好, Java!" 编码为 UTF-8 字节。
// 逐行注释:4. 关闭 Writer (它会自动关闭底层的 fos)。
writer.close();
(3) 详细对比表
| 特性 (Feature) | OutputStream (字节输出流) | Writer (字符输出流) |
| 处理单元 | 字节 (Byte) | 字符 (Character) |
基本 write() 方法 |
write(int b) / write(byte[] b) |
write(int c) / write(char[] c) / write(String s) |
| 主要用途 | 任何原始数据(图片、二进制文件) | 纯文本数据(.txt, .xml, .java) |
| 是否处理编码 | 否。你必须手动编码 str.getBytes(charset) |
是。它自动使用指定的字符集(Charset)编码。 |
| 常见子类 | FileOutputStream, ByteArrayOutputStream |
FileWriter, StringWriter, OutputStreamWriter |
| 对应的输入流 | InputStream (字节输入流) |
Reader (字符输入流) |
结论:
-
要保存图片、生成 Zip 包、或进行底层网络编程:用
OutputStream。 -
要生成文本文件(HTML, JSON, TXT, Properties):用
Writer,并始终指定编码(如 UTF-8)。
更多推荐

所有评论(0)