1.InputStream Reader 之间的区别

这是 Java I/O 中最基础也是最重要的区别之一。简单来说:

  • InputStream(字节流):处理原始的字节 (byte) 数据。

  • Reader(字符流):处理文本字符 (char) 数据。

Reader 知道如何将 InputStream 中的字节解码为字符。

(1) 核心区别:字节 vs 字符

  1. 字节 (Byte):

    • InputStreamread() 方法一次读取 1 个字节 (8-bit)。

    • 它完全不关心这些字节代表什么。一个英文字母 'A' 是 1 字节,一个汉字 '你' 在 UTF-8 编码下可能是 3 字节。

    • 用途:适用于所有类型的文件,特别是二进制文件,如图片 (.jpg), 视频 (.mp4), 可执行文件 (.exe)。

  2. 字符 (Character):

    • Readerread() 方法一次读取 1 个字符 (Java 内部使用 16-bit char)。

    • 它内部包含了一个解码器。它会从底层的字节流中读取一个或多个字节(例如,在 UTF-8 中读取 3 字节),然后将它们“组装”成一个完整的字符 '你'。

    • 用途专用于处理文本数据 (.txt, .java, .xml, .json)。

(2) 桥梁:InputStreamReader

InputStreamReader 不是非此即彼的,它们是协同工作的。

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


接下来,您是想了解 OutputStreamWriter 之间的区别(这与 InputStreamReader 正好对应)

这是一个绝佳的问题,它触及了 Java I/O 体系的另外两个核心概念,以及新旧 I/O 模型的演进。

我们来逐一分解。


2. 📤 OutputStreamWriter 之间的区别

这个区别与 InputStreamReader 的区别是完全对应的。一个是写字节,一个是写字符。

  • OutputStream(字节输出流):用于写入原始的字节 (byte)

  • Writer(字符输出流):用于写入文本字符 (char)

Writer 知道如何将 Java 内部的字符编码 (Encode) 为字节,然后交给底层的 OutputStream 去写入。

(1) 核心区别:编码 (Encoding)

  1. OutputStream (字节流)

    • 它的 write() 方法接受 int(只写低8位)、byte[]

    • 它不关心你给它的字节是什么含义。它只是一个“字节管道”。

    • 如果你想用它来写文本(比如 "你好"),你必须手动将字符串编码为字节数组,并处理 UnsupportedEncodingException

    • 用途:写入二进制数据,如保存图片、写入 .zip 文件、网络套接字传输原始数据。

  2. 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)。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐