目录
- 简介:
Scanner是什么? - 基本用法:如何读取基本数据类型(整数、小数、字符串等)。
- 进阶用法:读取一行、读取特定分隔符、判断是否有输入。
- 重要注意事项:
Scanner的常见陷阱和最佳实践。 - 完整代码示例
- 关闭
Scanner - 替代方案:
BufferedReader简介
简介
Scanner 是 Java java.util 包中的一个类,它像一个“扫描仪”,可以解析原始类型和字符串的文本,默认情况下,它会从标准输入流(System.in)读取,也就是你的键盘。
核心思想:Scanner 会将输入的文本分割成一个个“令牌”(token),然后你可以根据需要将这些令牌转换成不同的数据类型。
基本用法
要使用 Scanner,首先需要创建它的实例。
import java.util.Scanner; // 1. 导入 Scanner 类
public class ScannerDemo {
public static void main(String[] args) {
// 2. 创建一个 Scanner 对象,用于接收从键盘的输入
Scanner scanner = new Scanner(System.in);
// --- 读取不同类型的数据 ---
// a) 读取一个整数
System.out.print("请输入一个整数: ");
int intValue = scanner.nextInt(); // 读取下一个整数类型的令牌
System.out.println("你输入的整数是: " + intValue);
// b) 读取一个小数 (浮点数)
System.out.print("请输入一个小数: ");
double doubleValue = scanner.nextDouble(); // 读取下一个 double 类型的令牌
System.out.println("你输入的小数是: " + doubleValue);
// c) 读取一个单词 (以空格为分隔符)
System.out.print("请输入一个单词: ");
String word = scanner.next(); // 读取下一个单词类型的令牌
System.out.println("你输入的单词是: " + word);
// d) 读取一行文本 (包括空格)
System.out.print("请输入一行文本: ");
scanner.nextLine(); // **重要!消耗掉 nextInt() 后留下的换行符**
String line = scanner.nextLine(); // 读取一整行
System.out.println("你输入的一行是: " + line);
scanner.close(); // 3. 用完后关闭 Scanner
}
}
执行流程示例:
请输入一个整数: 100
你输入的整数是: 100
请输入一个小数: 3.14
你输入的小数是: 3.14
请输入一个单词: Hello
你输入的单词是: Hello
请输入一行文本: 你好,世界!
你输入的一行是: 你好,世界!
进阶用法
a) 读取一行 (nextLine())
next() 方法在遇到空格、Tab 或回车时会停止,而 nextLine() 会读取整行,直到遇到回车符。
b) 处理 next() 和 nextLine() 之间的换行符问题 (非常重要!)
这是一个最常见的 Scanner 陷阱。
问题场景:
当你使用 nextInt(), nextDouble(), next() 等方法后,它们只会读取数字或单词,但不会消耗掉用户输入后按下的 回车键 (\n),这个回车符会留在输入缓冲区中。
如果你紧跟着调用 nextLine(),nextLine() 会认为这个回车符是一个“空行”,并直接读取它,然后返回,导致你的程序跳过了用户输入的提示。
解决方案:
在调用 nextLine() 之前,如果前面使用了 nextInt() 等方法,先额外调用一次 scanner.nextLine() 来“消耗掉”那个多余的换行符。
// ... 代码接续上面的示例 ...
System.out.print("请输入你的年龄: ");
int age = scanner.nextInt();
System.out.println("年龄是: " + age);
// 如果这里直接写 scanner.nextLine(),它会读取上面按下的回车
// 所以我们需要先消耗掉它
scanner.nextLine(); // 关键代码!
System.out.print("请输入你的家庭住址: ");
String address = scanner.nextLine();
System.out.println("家庭住址是: " + address);
c) 判断是否有输入 (hasNextXXX())
有时候你不知道用户是否会输入,或者想检查下一个输入是否符合预期,这时可以使用 hasNextInt(), hasNextDouble(), hasNext() 等方法。
System.out.print("请输入一个整数(或直接回车退出): ");
if (scanner.hasNextInt()) {
int num = scanner.nextInt();
System.out.println("你输入的整数是: " + num);
} else {
System.out.println("你没有输入有效的整数。");
}
重要注意事项和最佳实践
-
资源泄漏:
Scanner是一个实现了AutoCloseable接口的类,它会占用系统资源(如文件句柄或输入流),如果长时间不关闭,可能会导致资源泄漏。- 最佳实践:使用
try-with-resources语句,这样可以自动关闭Scanner,即使发生异常也能保证资源被释放。
- 最佳实践:使用
-
异常处理:如果用户输入了不符合要求的数据(在
nextInt()时输入了 "abc"),程序会抛出InputMismatchException,在实际应用中,你应该使用try-catch块来处理这种异常,防止程序崩溃。
完整代码示例 (结合最佳实践)
下面是一个更健壮、更完整的示例,它结合了 try-with-resources 和异常处理。
import java.util.InputMismatchException;
import java.util.Scanner;
public class RobustScannerExample {
public static void main(String[] args) {
// 使用 try-with-resources,自动关闭 Scanner
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("--- 开始交互式输入 ---");
// 读取整数
while (true) {
try {
System.out.print("请输入你的年龄 (整数): ");
int age = scanner.nextInt();
if (age > 0 && age < 150) {
System.out.println("年龄记录成功: " + age);
break; // 输入正确,退出循环
} else {
System.out.println("年龄输入不合理,请重新输入。");
}
} catch (InputMismatchException e) {
System.out.println("输入错误:请输入一个有效的整数!");
scanner.next(); // 清除错误的输入,防止死循环
}
}
// 消耗掉 nextInt() 留下的换行符
scanner.nextLine();
// 读取一行文本
System.out.print("请输入你的名字: ");
String name = scanner.nextLine();
System.out.println("你好, " + name + "!");
System.out.println("--- 输入结束 ---");
} // scanner.close() 会在这里自动被调用
}
}
关闭 Scanner
如上所述,Scanner 占用资源,应该被关闭。
// 方式一:手动关闭 (不推荐,容易忘记)
Scanner scanner = new Scanner(System.in);
// ... 使用 scanner ...
scanner.close(); // 必须记得调用
// 方式二:使用 try-with-resources (强烈推荐)
try (Scanner scanner = new Scanner(System.in)) {
// ... 使用 scanner ...
} // 代码块执行完毕后,scanner.close() 会自动执行
注意:一旦关闭了 Scanner,它就不能再被使用了,再次调用其任何方法都会抛出 IllegalStateException。
替代方案:BufferedReader
虽然 Scanner 非常方便,但在性能要求极高的场景下(处理大量数据输入),BufferedReader 通常比 Scanner 更快。
BufferedReader 的特点是:
- 速度快:它有一个缓冲区,可以一次性读取大量数据,效率更高。
- 功能相对单一:它只能读取字符串(
readLine()),不像Scanner那样能直接解析成基本数据类型,如果需要转换,需要手动调用Integer.parseInt(),Double.parseDouble()等方法。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BufferedReaderExample {
public static void main(String[] args) {
// System.in 是字节流,BufferedReader 是字符流,需要用 InputStreamReader 转换
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
System.out.print("请输入一行文本: ");
String line = reader.readLine(); // 直接读取一行
System.out.println("你输入的是: " + line);
System.out.print("请输入一个整数: ");
String numberStr = reader.readLine(); // 先读取为字符串
try {
int number = Integer.parseInt(numberStr); // 再手动转换
System.out.println("你输入的整数是: " + number);
} catch (NumberFormatException e) {
System.out.println("输入的不是有效的整数。");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
| 特性 | Scanner |
BufferedReader |
|---|---|---|
| 易用性 | 高,可以直接解析 int, double 等 |
低,只能读字符串,需手动转换 |
| 性能 | 较慢 | 高,有缓冲区,适合大量数据 |
| 功能 | 丰富,可自定义分隔符 | 简单,主要就是读一行 |
| 适用场景 | 一般的控制台程序、交互式输入 | 性能敏感的输入场景、OJ刷题 |
给初学者的建议:
- 优先使用
Scanner,它简单、直观,能满足绝大多数日常开发需求。 - 牢记
nextLine()的陷阱,并学会用scanner.nextLine()来解决。 - 养成使用
try-with-resources的好习惯,确保资源被正确关闭。 - 当你遇到性能瓶颈或者需要处理超大规模输入时,再考虑使用
BufferedReader。
