java字符流乱码的根本原因是解码时charset与文件实际编码不一致;filereader/filewriter默认使用系统编码(如windows的gbk),易致跨平台乱码;应显式用inputstreamreader/outputstreamwriter指定标准iana编码名如"utf-8"。

Java中Reader与Writer字符流出现乱码,根本原因不是“读错了字”,而是“认错了编码门牌号”——解码时用的字符集和文件实际编码不一致。关键不在流本身,而在它背后隐含或显式指定的Charset。
FileReader/FileWriter默认编码陷阱
这两个类看似方便,实则埋雷:它们底层调用InputStreamReader/OutputStreamWriter,但硬编码使用系统默认Charset(Windows通常是GBK,Linux/macOS多为UTF-8)。同一份UTF-8文件,在Windows上用FileReader读会变成“???”,在Linux上却可能正常——这不是代码问题,是环境差异暴露了隐式依赖。
- 永远不要直接用FileReader读含中文的文本文件,除非你100%确认文件编码=当前系统file.encoding
- 用
System.getProperty("file.encoding")可查当前JVM默认编码,但它不可靠,不应作为依据 - 替代方案:用
InputStreamReader(new FileInputStream(f), "UTF-8")显式锁定编码
InputStreamReader/OutputStreamWriter编码参数写法细节
显式指定编码看似简单,但大小写、连字符、拼写错误都会导致失败。JVM只认标准IANA名称,且区分大小写。
- ✅ 正确写法:
"UTF-8"(带短横线,大写U、T、F,数字8) - ❌ 常见错误:
"utf8"、"UTF8"、"utf_8"→ 抛UnsupportedEncodingException - 查真实编码:Linux/macOS用
file -i filename;Windows可用VS Code打开,右下角显示当前识别编码
BufferedReader/BufferedWriter只是性能包装,不解决编码问题
它们本身不参与编码转换,只是对Reader/Writer加了一层缓冲。乱码与否,完全取决于底层Reader/Writer构造时传入的Charset是否匹配文件真实编码。
-
new BufferedReader(new InputStreamReader(is, "UTF-8"))—— 编码逻辑在InputStreamReader里 - BufferedReader的
readLine()返回的是已正确解码的String,无需再做new String(bytes, "UTF-8") - 如果仍乱码,说明问题出在InputStreamReader的Charset参数,而不是BufferedReader
写入时BOM与跨平台兼容性权衡
UTF-8标准本身不要求BOM,但Windows记事本依赖它识别编码。Java默认不写BOM,这是合理设计;强行添加反而引发新问题。
- 服务端生成JSON、CSV、XML等格式时,绝对不要写BOM——会导致前端JSON.parse()报错"Unexpected token \uFEFF"
- 若必须兼容记事本,可手动写入
"\uFEFF",但前提是OutputStreamWriter明确设为"UTF-8" - 更推荐方案:
Files.newBufferedWriter(Paths.get("f.txt"), StandardCharsets.UTF_8),简洁、安全、无BOM
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











