
Scanner.reset() 方法的核心作用
scanner.reset() 方法的主要目的是将 scanner 实例的显式状态信息恢复到其初始默认值。根据 oracle 官方文档的描述,当一个 scanner 被重置时,它会丢弃所有可能通过调用 usedelimiter(java.util.regex.pattern)、uselocale(java.util.locale) 或 useradix(int) 方法而改变的显式状态信息。
具体来说,reset() 方法会执行以下操作:
- 重置分隔符模式: 如果使用 useDelimiter() 方法更改了分隔符,reset() 会将其恢复为默认的空白字符模式(\p{javaWhitespace}+)。
- 重置区域设置: 如果使用 useLocale() 方法更改了用于数字和日期解析的区域设置,reset() 会将其恢复为默认的系统区域设置。
- 重置基数: 如果使用 useRadix() 方法更改了数字解析的基数(例如从十进制改为十六进制),reset() 会将其恢复为默认的十进制(基数10)。
需要注意的是,reset() 方法不会重置 Scanner 内部的缓冲区或底层输入流的读取位置。它只影响 Scanner 如何解释和解析后续输入。
何时 reset() 显得“无用”?
在文章开头提供的示例代码中,用户发现即使移除 s.reset(),程序也能正常运行。这正是因为在该示例代码中,Scanner 实例的 useDelimiter()、useLocale() 或 useRadix() 方法都没有被调用。换句话说,Scanner 的显式状态一直保持在默认值。在这种情况下,调用 reset() 实际上并没有任何状态可以“重置”,因此它不会对程序的行为产生任何可见影响。
例如,原始代码片段如下:
立即学习“Java免费学习笔记(深入)”;
package com.company;
import java.io.IOException;
import java.util.Scanner;
public class Favorite_Number {
public static void main(String[] args) throws IOException {
int X,sum = 0,rem = 0,t;
Scanner s = new Scanner(System.in);
t = s.nextInt();
while(t!=0) {
s.reset(); // <-- 在这里移除它,程序仍然正常工作
X = s.nextInt();
while (X > 0) {
rem = X % 10;
if (rem == 5) {
sum++;
}
X = X / 10;
}
System.out.println(sum);
sum = 0;
t--;
}
}
}由于 Scanner s 从未调用过 useDelimiter() 等方法来改变其解析状态,s.reset() 在这里是冗余的。
reset() 方法的实际应用示例
为了更好地理解 reset() 的作用,我们来看几个实际的例子。
示例一:重置分隔符
默认情况下,Scanner 使用空白字符作为分隔符。我们可以通过 useDelimiter() 改变它,然后用 reset() 恢复。
import java.util.Scanner;
public class ScannerDelimiterResetExample {
public static void main(String[] args) {
String input = "Hello,World!Java-Programming";
Scanner scanner = new Scanner(input);
System.out.println("--- 默认分隔符(空白字符) ---");
// 默认分隔符是空白字符,这里没有空白,所以整个字符串被视为一个token
System.out.println("Next token: " + scanner.next()); // 输出: Hello,World!Java-Programming
scanner.close(); // 关闭当前scanner
scanner = new Scanner(input); // 重新创建一个Scanner
System.out.println("\n--- 使用逗号、感叹号或连字符作为分隔符 ---");
scanner.useDelimiter("[,!\\-]+"); // 设置分隔符为逗号、感叹号或连字符
while (scanner.hasNext()) {
System.out.println("Next token: " + scanner.next());
}
// 输出:
// Next token: Hello
// Next token: World
// Next token: Java
// Next token: Programming
scanner.close(); // 关闭当前scanner
scanner = new Scanner(input); // 重新创建一个Scanner
System.out.println("\n--- 先设置分隔符,再使用 reset() 恢复默认 ---");
scanner.useDelimiter("[,!\\-]+"); // 设置分隔符
System.out.println("使用自定义分隔符获取第一个token: " + scanner.next()); // 输出: Hello
scanner.reset(); // 重置Scanner状态,分隔符恢复为默认的空白字符
System.out.println("重置后,使用默认分隔符获取下一个token: " + scanner.next()); // 输出: ,World!Java-Programming
// 因为重置后分隔符变回空白字符,而剩下的字符串中没有空白字符,
// 所以整个剩余部分被视为一个token。
scanner.close();
}
}在这个例子中,可以看到 scanner.reset() 成功地将分隔符从自定义的 [,!\\-]+ 恢复到了默认的空白字符,从而改变了后续 next() 方法的解析行为。
示例二:重置区域设置
Scanner 使用区域设置来解析数字和日期。例如,在某些区域设置中,小数分隔符是逗号而不是点。
import java.util.Locale;
import java.util.Scanner;
public class ScannerLocaleResetExample {
public static void main(String[] args) {
String input = "123.45 67,89";
Scanner scanner = new Scanner(input);
System.out.println("--- 默认区域设置(例如,en_US) ---");
// 假设默认区域设置使用 '.' 作为小数分隔符
System.out.println("Next double (default locale): " + scanner.nextDouble()); // 输出: 123.45
scanner.close(); // 关闭当前scanner
scanner = new Scanner(input); // 重新创建一个Scanner
System.out.println("\n--- 使用法国区域设置 (fr_FR) ---");
scanner.useLocale(Locale.FRENCH); // 法国区域设置使用 ',' 作为小数分隔符
System.out.println("Next double (French locale): " + scanner.nextDouble()); // 输出: 67.89
// 注意:这里由于input是"123.45 67,89",
// 默认Locale下,123.45是一个double,67,89则会解析失败或被视为两个token。
// 而在French Locale下,123.45会被解析为整数123,而67,89会被解析为double 67.89。
// 为了演示reset,我们简化一下输入。
scanner.close(); // 关闭当前scanner
String input2 = "123,45 67.89"; // 调整输入,以更好地演示
scanner = new Scanner(input2);
System.out.println("\n--- 先设置区域设置,再使用 reset() 恢复默认 ---");
scanner.useLocale(Locale.FRENCH); // 设置为法国区域设置
System.out.println("使用法国区域设置获取第一个double: " + scanner.nextDouble()); // 输出: 123.45
scanner.reset(); // 重置Scanner状态,区域设置恢复为默认
// 假设默认区域设置为en_US,此时 '.' 是小数分隔符
System.out.println("重置后,使用默认区域设置获取下一个double: " + scanner.nextDouble()); // 输出: 67.89
scanner.close();
}
}这个例子展示了 reset() 如何将 Scanner 的区域设置从 Locale.FRENCH 恢复到系统默认区域设置,从而影响了 nextDouble() 方法对数字字符串的解析方式。
使用 reset() 的注意事项
- 不影响输入流位置: reset() 方法仅重置 Scanner 的解析状态,不会改变它从底层输入源(如 System.in、File 或 String)读取数据的位置。这意味着,如果 Scanner 已经读取了一些数据,reset() 不会“回溯”到输入流的开头。
- 不关闭 Scanner: 调用 reset() 不会关闭 Scanner。Scanner 仍然可以继续从其输入源读取数据。
- 主要用于恢复默认解析行为: reset() 最有用的场景是在 Scanner 的解析行为被 useDelimiter()、useLocale() 或 useRadix() 临时修改后,需要将其恢复到初始的默认行为时。
- 与 InputStream.reset() 的区别: 不要混淆 Scanner.reset() 与 InputStream.reset()。后者是 InputStream 接口的一个方法,用于将输入流重置到上一个标记点(如果流支持并设置了标记),它确实会影响读取位置。两者功能完全不同。
总结
Scanner.reset() 方法是一个用于管理 java.util.Scanner 实例解析行为的重要工具。它允许开发者在修改了 Scanner 的分隔符、区域设置或基数后,方便地将其恢复到默认状态。在没有显式修改这些解析状态的情况下,调用 reset() 是冗余的,不会产生任何效果。理解其作用和适用场景,有助于编写更健壮、可预测的 Scanner 代码。










