OutputStreamWriter本身不乱码,乱码源于Charset参数未显式指定或与上下游解码不一致;应始终用StandardCharsets.UTF_8显式构造,确保编解码链统一,并同步规范IDE、文件保存及终端编码。

OutputStreamWriter 本身不存“乱码”,它只是按你给的 Charset 把字符转成字节。真正出问题的,是 Charset 参数没设对,或者设了却和上下游解码规则不一致。
Charset 参数不显式指定,就默认用系统编码
如果你写 new OutputStreamWriter(os),没传 Charset,JVM 就调用 Charset.defaultCharset() —— Windows 中常是 GBK,Linux/macOS 多为 UTF-8。同一段代码在不同机器上输出可能一个正常、一个全是问号或方块。这不是 bug,是默认行为不可控。
- 避免无参构造,始终显式传入
StandardCharsets.UTF_8 - 不要依赖
"UTF-8"字符串字面量,用StandardCharsets类更安全(类型检查+避免拼写错误) - 若必须兼容旧系统(如某些 Windows 服务只认 GBK),则明确传
Charset.forName("GBK"),但需同步告知读取方
写入端用了 UTF-8,读取端却用 GBK 解码
OutputStreamWriter 按 UTF-8 编码写出了 0xE4 0xB8 0xAD(“中”字),但 FileReader 或浏览器用 GBK 去解,就会把这三字节强行当两个 GBK 字符处理,结果变成“涓”或乱码符号。本质是编解码链条断裂。
- 文件场景:写入用
OutputStreamWriter(os, UTF_8),读取就必须配InputStreamReader(is, UTF_8) - Web 场景:响应体用 UTF-8 写,还得加
response.setContentType("text/html;charset=UTF-8")告诉浏览器怎么解 - 网络通信:TCP/HTTP 接口文档里必须明确定义字符集,客户端发前 encode,服务端收后 decode,双方必须一致
String.getBytes() 和 new String(byte[]) 暗藏编码陷阱
这两处最容易漏掉 Charset。比如 str.getBytes() 不带参数,会走系统默认编码;new String(bytes) 同样不指定 Charset,也会用默认解码 —— 这和 OutputStreamWriter 的默认行为叠加,错上加错。
- 写文件时别用
os.write(str.getBytes()),改用os.write(str.getBytes(UTF_8)) - 日志或调试中打印 byte[],别直接
new String(bytes),应写成new String(bytes, UTF_8) - 所有涉及字节与字符串互转的地方,都该把 Charset 当作必填参数来对待
IDE、文件保存、终端显示也要对齐 UTF-8
就算 Java 层全用 UTF-8,如果源码文件本身是 GBK 保存的,字符串字面量“你好”在编译时就被错误读入;或者终端(如 Windows cmd)不支持 UTF-8 显示,System.out 输出仍是乱码 —— 这些属于环境链路的缺口。
- IDE 设置项目编码为 UTF-8(IntelliJ:File → Settings → Editor → File Encodings)
- 保存 .java 文件时确认编码是 UTF-8(VS Code 右下角可切换)
- Windows 终端启用 UTF-8:执行
chcp 65001,或在属性中勾选“使用 Unicode UTF-8 提供全球语言支持”


















