Scanner读中文乱码的根本原因是终端、IDE、JVM编码不一致,必须显式指定StandardCharsets.UTF_8;同时需验证System.in有效性、统一换行符处理、测试时getBytes()须指定UTF-8编码。

Scanner默认用系统编码,Windows中文环境常是GBK,但IDE或终端可能是UTF-8
Java的Scanner构造函数不显式指定编码时,会调用System.in的默认字符集,而System.in又依赖JVM启动时读取的系统属性——不是你代码里写的源文件编码,也不是IDE设置的项目编码。Windows命令行(cmd)默认GBK,IntelliJ默认UTF-8,VS Code终端可能又是UTF-8,三者一错位,输入中文就变???或抛InputMismatchException。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 永远显式传入
Charset给Scanner构造器,别依赖默认 - 优先用
StandardCharsets.UTF_8——除非你明确要兼容老旧GBK终端(比如纯cmd且不改chcp) - 确认终端/IDE真正使用的编码:在cmd输
chcp,在IDE里查“Terminal Encoding”或“Console Encoding”设置
new Scanner(System.in, "UTF-8") 不生效?检查JVM是否禁用了标准输入重定向
有些IDE(如老版本Eclipse)或打包后的jar双击运行时,System.in可能被重定向为null或非真实流,此时Scanner构造不报错但读不到内容,或读到空字符串——看起来像乱码,其实是根本没读进来。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 先验证
System.in是否可用:System.out.println(System.in != null); - 避免双击jar运行;用命令行:
java -jar app.jar,确保终端编码与代码中指定的一致 - 如果必须支持GUI双击,改用
JOptionPane.showInputDialog等Swing组件替代控制台输入
Scanner.nextLine() 读到空行?和换行符编码有关
Windows用\r\n,Unix系用\n,而Scanner的nextLine()按\n切分。如果终端发来的是\r\n但JVM底层处理异常(尤其在某些Docker容器或WSL环境下),可能导致nextLine()提前截断、返回空串,后续读取偏移错乱——表面像乱码,实则是逻辑错位。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 统一用
next()读单词、nextLine()读整行,但调用前确保前一个token已消费完(比如nextInt()后漏了nextLine()清缓冲区) - 调试时打印原始字节:
byte[] buf = new byte[100]; int len = System.in.read(buf); System.out.println(Arrays.toString(Arrays.copyOf(buf, len))); - 生产环境尽量避免混合使用
nextXxx()和nextLine(),优先用nextLine()再自行解析
Spring Boot或JUnit里测Scanner输入,中文还是乱码?注意测试上下文的System.in
单元测试中常用System.setIn(new ByteArrayInputStream("你好".getBytes(StandardCharsets.UTF_8)))模拟输入,但如果"你好".getBytes()没指定编码,会走平台默认编码(Windows下是GBK),导致字节数组本身就不对,Scanner再设UTF-8也白搭。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 所有
getBytes()调用必须带StandardCharsets.UTF_8参数 - JUnit 5可用
@ExtendWith(MockedStaticExtension.class)临时mockSystem.in,但更简单的是直接传Scanner实例进被测方法,把输入解耦出来 - Spring Boot集成测试若用
SpringApplication.run()启动,System.in仍是原始流——别指望测试类里的setIn能影响它
最常被忽略的点:你以为改了代码里的Scanner构造参数就完了,但终端、IDE、JVM启动参数(如-Dfile.encoding=UTF-8)、甚至Shell本身的locale设置,全在暗处参与编码决策。调不通时,先看chcp或locale输出,比翻源码快得多。











