Java 输入输出避坑指南:从控制台交互到文件读写,一篇搞定所有实战问题
一、引言
你是不是也在 Java 输入输出上栽过跟头?用nextInt()后接nextLine(),明明没输入却多出空行;想把浮点数格式化成金额,结果显示一长串小数;读取文件时要么报 “路径错误”,要么找不到文件…… 其实这些问题都不是 “玄学”,而是没摸透 Java IO 的底层逻辑 ——Scanner的方法特性、printf的格式规则、文件路径的转义要求,每一个细节都藏着 “正确用法”。今天这篇博客,就从控制台输入的 “换行符坑” 讲起,到格式化输出的定制技巧,再到文件读写的路径与异常处理,把 Java 输入输出的核心知识点和实战坑点全拆透,让你看完就能避开 90% 的 IO 错误,写交互和文件代码时再也不用反复查手册!
二、控制台输入 —— 用Scanner类读取用户输入
Scanner是 Java 处理控制台输入的核心类,它提供了多种方法读取不同类型的数据(整数、字符串、浮点数等),但需注意方法间的配合问题。
1. Scanner核心输入方法:分类与实战示例
Scanner通过封装输入流(如System.in,对应控制台输入)实现数据读取,常用方法可按 “读取数据类型” 分类,以下是完整整理及示例:
|
方法 |
功能描述 |
示例代码 |
输入示例 |
输出结果 |
|---|---|---|---|---|
|
|
读取整行内容(包括空格,以 |
|
今天天气晴 |
今天天气晴 |
|
|
读取下一个 “单词”(以空格 / |
|
Hello World |
Hello |
|
|
读取并转换为整数 |
|
123 |
133 |
|
|
读取并转换为浮点数 |
|
3.14 |
6.28 |
|
|
检测输入流中是否还有 “单词” |
|
Java IO |
Java |
|
|
检测输入流中是否还有整数 |
|
45 |
50 |
|
|
检测输入流中是否还有浮点数 |
|
2.5 |
4.0 |
2. 实战坑点:nextInt()与nextLine()的 “残留换行符” 问题
当nextInt()与nextLine()配合使用时,会因 “换行符残留” 导致nextLine()读取空内容,这是初学者最常踩的坑。问题复现:
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) {
Scanner a = new Scanner(System.in); // 创建Scanner对象,直接打印会输出哈希码
int num = a.nextInt(); // 读取整数,残留换行符
String str1 = a.nextLine(); // 读取到残留的换行符,返回空串
String str2 = a.nextLine(); // 正常读取第二次输入
System.out.println(num);
System.out.println(str1);
System.out.println(str2);
}
}

-
原因解析:
-
nextInt()仅读取整数部分(如123),不会读取输入末尾的\n(Enter键产生),导致\n残留在输入流中; -
nextLine()会读取 “当前位置到下一个\n” 的内容,遇到残留的\n时,直接返回空串。
-
两种解决方案
方案 1:用nextLine()读取整数(再手动转类型)将 “读取整数” 也通过nextLine()实现,避免换行符残留,步骤:
-
用
nextLine()读取整行字符串(如"123"); -
通过
Integer.parseInt(字符串)将其转为int类型。
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) {
Scanner a = new Scanner(System.in);
int num = Integer.parseInt(a.nextLine()); // 读取整行转整数,无残留
String str1 = a.nextLine(); // 正常读取
String str2 = a.nextLine();
System.out.println(num);
System.out.println(str1);
System.out.println(str2);
}
}
方案 2:若坚持用nextInt()读取整数,可在其后额外调用一次a.nextLine(),专门读取残留的\n,步骤:
-
nextInt()读取整数后,调用a.nextLine()(不接收结果,仅 “消费”\n); -
后续
nextLine()正常读取内容。
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) {
Scanner a = new Scanner(System.in);
int num = a.nextInt(); // 读取整数
a.nextLine(); // 关键:吃掉残留的\n
String str1 = a.nextLine(); // 正常读取
String str2 = a.nextLine();
System.out.println(num);
System.out.println(str1);
System.out.println(str2);
}
}
三、格式化输出 —— 用printf与String.format定制输出格式
默认的System.out.print()输出格式粗糙(如浮点数显示过多小数位),而printf方法(沿用 C 语言风格)可通过 “格式说明符” 定制输出,满足金额、日期等场景的格式化需求。
1. printf核心语法:格式说明符 + 参数
printf的语法为:System.out.printf("格式字符串", 参数1, 参数2, ...),其中 “格式字符串” 包含普通文本和 “%开头的格式说明符”(如%d表示整数,%s表示字符串)。
(1)常用转换符(指定数据类型)
|
转换符 |
数据类型 |
示例 |
输出结果 |
|---|---|---|---|
|
|
十进制整数 |
|
159 |
|
|
字符串 |
|
Hello Java |
|
|
字符 |
|
H |
|
|
布尔值 |
|
false |
|
|
定点浮点数 |
|
3333.33 |
|
|
指数浮点数 |
|
1.59e+01 |
|
|
十六进制整数 |
|
9f |
|
|
八进制整数 |
|
237 |
|
|
百分号(转义) |
|
99.50% |
|
|
散列码 |
|
42628b2 |
|
|
日期时间(T 强制大写) |
|
Mon Feb 09 18:05:19 PST 2015 |
|
|
平台相关行分隔符 |
|
a(换行)b |
(2)常用标志(定制格式细节)
|
标志 |
目的 |
示例 |
输出结果 |
|---|---|---|---|
|
|
显示正负号 |
|
+3333.33 |
|
正数前加空格 |
|
3333.33(前有空格) |
|
|
|
数字前补 0 |
|
003333.33 |
|
|
左对齐 |
|
3333.33 (后有空格) |
|
|
负数用括号包裹 |
|
(3333.33) |
|
|
添加分组分隔符(千分位) |
|
3,333.33 |
|
|
浮点数必含小数点 |
|
3333. |
|
|
十六进制加 |
|
0x9f |
|
|
指定参数索引 |
|
159 9f |
|
|
复用前一个参数 |
|
159 9f |
(3)日期时间格式化(%t系列转换符)
虽推荐用java.time包处理日期,但遗留代码中仍可能用到printf的日期格式化,通过 “%t+ 子转换符” 实现,文档中表 3-7 列出核心子转换符,整理常用场景:
|
转换符 |
日期时间类型 |
示例 |
输出结果 |
|---|---|---|---|
|
|
完整日期时间 |
|
Mon Feb 09 18:05:19 PST 2015 |
|
|
ISO 8601 日期(年 - 月 - 日) |
|
2015-02-09 |
|
|
美国格式日期(月 / 日 / 年) |
|
02/09/2015 |
|
|
24 小时时间(时:分: 秒) |
|
18:05:19 |
|
|
24 小时时间(时:分) |
|
18:05 |
|
|
12 小时时间(时:分: 秒 上午 / 下午) |
|
06:05:19 pm |
|
|
4 位年份 |
|
2015 |
|
|
月的完整拼写 |
|
February |
|
|
星期的完整拼写 |
|
Monday |
|
|
两位数字的日 |
|
09 |
2. String.format:创建格式化字符串(不直接输出)
若需将格式化结果保存为字符串(而非直接打印),可使用String.format方法,语法与printf一致,返回值为格式化后的字符串。
String name = "张三";
int age = 25;
String message = String.format("Hello, %s. Next year, you'll be %d", name, age + 1);
System.out.println(message); // 输出:Hello, 张三. Next year, you'll be 26
四、文件输入输出 —— 读取文件内容与处理路径
除控制台交互外,Java 还支持读写文件,核心是用Scanner读取文件(输入)、PrintWriter写入文件(输出),需注意路径处理和 IO 异常。
1. 文件输入:用Scanner读取文件内容
读取文件需先构造 “指向文件的Scanner对象”,依赖java.nio.file.Paths获取文件路径,步骤如下:
import java.util.Scanner;
import java.nio.file.Paths;
import java.io.IOException; // 处理IO异常
public class Demo2 {
public static void main(String[] args) {
try {
// 构造文件路径(绝对路径:D盘桌面的myfile.txt,注意反斜杠转义)
Scanner in = new Scanner(Paths.get("D:\\桌面\\myfile.txt"), "UTF-8");
// 逐行读取文件内容并打印
while (in.hasNextLine()) {
System.out.println(in.nextLine());
}
-
IO 异常处理(完整逻辑)
读取文件时可能出现 “文件不存在”“权限不足” 等异常,Java 要求显式处理,除了在main方法声明throws IOException,更规范的方式是用try-catch捕获异常,确保程序遇到错误时能友好提示:
import java.util.Scanner;
import java.nio.file.Paths;
import java.io.IOException;
public class Demo2 {
public static void main(String[] args) {
try {
// 1. 构造文件路径(修正路径:去掉末尾多余反斜杠,避免路径解析错误)
Scanner in = new Scanner(Paths.get("D:\\桌面\\myfile.txt"), "UTF-8");
// 2. 逐行读取文件内容(循环判断是否有下一行,避免读取越界)
while (in.hasNextLine()) {
System.out.println(in.nextLine()); // 打印文件每行内容
}
// 3. 关闭输入流(释放文件资源,避免资源泄漏)
in.close();
} catch (IOException e) {
// 捕获IO异常,打印错误信息(便于定位问题)
e.printStackTrace();
System.out.println("文件读取出错:" + e.getMessage());
}
}
}
运行结果:若myfile.txt中内容为 “今天天气很好,我的心情也很好。”,控制台会准确输出该内容;若文件不存在,会打印 “文件读取出错:D:\ 桌面 \myfile.txt (系统找不到指定的文件。)”。
-
文件路径定位技巧
获取当前工作目录:用System.getProperty("user.dir")打印 Java 虚拟机启动路径,确认相对路径的基准目录,示例:
String currentDir = System.getProperty("user.dir");
System.out.println("当前工作目录:" + currentDir); // 输出如“D:\JavaProjects\Demo”
绝对路径规范:Windows 系统路径需用\\转义(如D:\\桌面\\myfile.txt),Linux/Mac 系统用/(如/home/user/myfile.txt),避免因路径分隔符错误导致文件找不到。
命令行重定向:若用命令行启动程序,可通过java 类名 < 输入文件 > 输出文件简化文件读写,无需处理IOException,示例:
java Demo2 < D:\桌面\myfile.txt > D:\桌面\output.txt
2. 文件输出:用PrintWriter写入文件内容
除了读取文件,Java 还支持用PrintWriter向文件写入内容,语法与System.out类似,可直接调用print()/println()方法。
import java.io.PrintWriter;
import java.io.IOException;
public class FileWriteDemo {
public static void main(String[] args) {
try {
// 1. 创建PrintWriter对象,指定写入的文件路径(若文件不存在则自动创建)
PrintWriter out = new PrintWriter("D:\\桌面\\output.txt");
// 2. 向文件写入内容(支持字符串、整数、浮点数等类型)
out.println("今天学习了Java文件输出");
out.print("整数:");
out.println(123);
out.print("浮点数:");
out.printf("%.2f", 3.1415); // 支持格式化写入
// 3. 关闭输出流(必须关闭,否则内容可能未写入文件)
out.close();
System.out.println("内容写入完成!");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件写入出错:" + e.getMessage());
}
}
}
-
关键注意点:
-
PrintWriter创建时,若文件已存在,会覆盖原有内容; -
必须调用
close()方法或flush()方法,否则缓冲区内容不会写入文件(close()会自动调用flush())。
-
3. 文件读写核心总结
|
操作类型 |
核心类 / 方法 |
关键注意点 |
|---|---|---|
|
文件输入 |
|
路径需转义,处理 IO 异常,读取后关闭流 |
|
文件输出 |
|
覆盖原有内容,关闭流确保写入,支持格式化 |
|
路径定位 |
|
确认相对路径基准,绝对路径区分操作系统 |
|
异常处理 |
|
捕获文件不存在、权限不足等异常,便于调试 |
结语
从Scanner的 “残留换行符” 解决方案,到printf用转换符和标志定制输出格式,再到文件读写的路径处理与异常捕获,我们终于把 Java 输入输出的 “实战逻辑” 理清楚了。其实 IO 操作看似琐碎,却藏着程序与外界交互的核心逻辑 —— 控制台输入是 “听用户说话”,格式化输出是 “把话说清楚”,文件读写是 “和硬盘打交道”。下次写代码时,当你用nextLine()读取整数、用%,.2f格式化金额、用Paths.get()定位文件,你会发现这些操作都有章可循。把这篇指南收起来,下次遇到 IO 问题翻一翻,让每一次输入输出都稳稳当当。如果还想深挖更多 Java 实战技巧,关注我,后续继续拆解开发中那些 “一看就懂、一用就会” 的核心知识!
更多推荐



所有评论(0)