转换流是字节与字符间的编解码桥梁:inputstreamreader按指定charset解码字节为unicode字符,outputstreamwriter按指定charset编码字符为字节;必须显式指定编码以避免乱码,且编解码需严格配对。

转换流(InputStreamReader 和 OutputStreamWriter)本质上不是“流”本身,而是字节与字符之间的编解码桥梁——它不直接读写数据,而是把底层字节流的原始二进制数据,按指定规则“翻译”成可读字符,或把字符“转译”成目标编码的字节。
它为什么必须是“解码器”和“编码器”?
因为字节流只认 0/1,不认识“中文”“换行”“emoji”;字符流却要处理“一个汉字是字符、占两个 char 单位、但 UTF-8 下可能存为 3 个字节”。这种语义鸿沟必须靠明确的编解码逻辑来弥合:
- InputStreamReader 是解码器:从字节流(如 FileInputStream)中读出原始字节 → 按构造时指定的 charset(如 "GBK")把连续字节组合、识别、还原成 Unicode 字符 → 输出给上层字符操作(如 read()、readLine())
- OutputStreamWriter 是编码器:接收上层传来的字符(char 或 String)→ 按指定 charset(如 "UTF-8")把每个字符查表转成对应字节序列 → 写入到底层字节流(如 FileOutputStream)
不指定编码,就等于放弃控制权
如果只用 FileReader/FileWriter,它们内部也用了转换流,但默认调用 Charset.defaultCharset()——也就是操作系统当前编码(Windows 中文版通常是 GBK,Linux/macOS 通常是 UTF-8)。一旦文件编码与系统默认不一致,比如用记事本(GBK)存的中文文件,被 IDE(UTF-8 环境)里的 FileReader 打开,就会乱码。而 InputStreamReader(new FileInputStream(f), "GBK") 显式声明了解码规则,就绕过了默认陷阱。
编码和解码必须配对才可靠
就像寄信要统一用同一种密码本:
- 用 UTF-8 编码写入 → 必须用 UTF-8 解码读出
- 用 GBK 编码写入 → 必须用 GBK 解码读出
- 混用(如 UTF-8 写 + GBK 读)会导致部分字节被错误切分,出现“鎴戠埍java”这类典型乱码
它让缓冲和文本处理成为可能
纯字节流(InputStream/OutputStream)没有 readLine()、不能按“字符”跳过 BOM、也不理解换行符差异(\n vs \r\n)。而转换流包装后,上层获得的是标准字符流接口,配合 BufferedReader 就能自然分行;配合 BufferedWriter 就能用 newLine() 统一换行。这背后全是它在默默完成:字节块 → 解码为字符序列 → 拆分成行 → 缓冲输出 → 再编码回字节块。










