1. 概述

在Java中,我们可以使用Integer.parseInt(Scanner.nextLine())Scanner.nextInt()两种方式从Scanner读取整数。然而,这两种方法之间存在一些差异。

在这个教程中,我们将比较它们,并讨论它们的不同之处。

2. 使用Integer.parseInt(scanner.nextLine())scanner.nextInt()读取整数

Scanner.nextLine()方法会从扫描器读取整个行作为字符串。因此,如果我们希望结果是Integer类型,我们必须自己将字符串转换为Integer,例如使用Integer.parseInt()方法。

另一方面,Scanner.nextInt()会读取输入流中的下一个标记作为整数Scanner中的一个标记由解析输入流时使用的分隔模式定义。默认情况下,Scanner的分隔模式是任何空白字符(如空格、制表符或换行符)。

现在我们了解了这两个方法的作用。接下来,让我们看看它们如何从Scanner对象读取整数。首先,我们给扫描器提供一个带有换行符的字符串:

String input = "42\n";

为了简化,我们在本教程中使用单元测试断言来验证结果。

首先,使用Scanner.nextLine()获取数字42:

Scanner sc1 = new Scanner(input);
int num1 = Integer.parseInt(sc1.nextLine());
assertEquals(42, num1);

接下来轮到Scanner.nextInt()

Scanner sc2 = new Scanner(input);
int num2 = sc2.nextInt();
assertEquals(42, num2);

正如上面的两个测试所示,两种方法都能从Scanner对象正确地读取输入并获取整数42。

下面我们来看看这两种方法之间的区别。

3. 当输入格式不正确时

它们的第一个差异在于,当输入不是一个有效的数字时,两种方法会抛出不同的异常。

现在,改变输入:

String input = "Nan\n";

当我们使用scanner.nextLine()读取输入并尝试将无效输入转换为整数时,它会抛出**NumberFormatException**:

Scanner sc1 = new Scanner(input);
assertThrows(NumberFormatException.class, () -> Integer.parseInt(sc1.nextLine()));

这是因为sc1.nextLine()将读取下一行输入作为字符串。但后续的转换失败,所以Integer.parseInt()方法抛出了NumberFormatException

值得一提的是,我们使用了JUnit 5的assertThrows()方法来断言方法调用会抛出NumberFormatException

另一方面,当我们尝试使用Scanner.nextInt()读取输入作为整数时,它会引发**InputMismatchException**:

Scanner sc2 = new Scanner(input);
assertThrows(InputMismatchException.class, sc2::nextInt);

4. Scanner.nextInt()方法不会消耗无效的标记

如前所述,Scanner.nextLine()会读取输入流中的下一行作为字符串。与Scanner.nextLine()不同,nextInt()会尝试将输入流中的下一个标记解析为整数。

如果它无法解析一个标记,如之前所述,nextInt()会抛出InputMismatchException。但值得注意的是,Scanner.nextInt()并不会消费解析失败的标记。

下面的例子可以帮助我们快速理解这一点:

String input = "42 is a magic number\n";

// nextInt()
Scanner sc2 = new Scanner(input);
int num2 = sc2.nextInt();
assertEquals(42, num2);

// calling nextInt() again on "is" raises the exception
assertThrows(InputMismatchException.class, sc2::nextInt);

String theNextToken = sc2.next();
assertEquals("is", theNextToken);

如上例所示,输入字符串有五个标记。第一个是“42",这是一个有效的整数。正如预期,sc2.nextInt()获取了整数42。然后,我们再次调用nextInt()并尝试将标记“is"解析为整数。这会抛出InputMismatchException,这也是我们的预期。

接下来,我们调用sc2.next()获取下一个标记作为字符串。我们可以看到,刚才nextInt()方法未能解析的标记“is"被读取到了输入中。换句话说,“is"并没有被sc2.nextInt()方法消费。

5. 换行处理

Java Scanner默认通过“\n”字符分隔行。接下来,让我们看看Scanner.nextLine()nextInt()如何处理换行字符。

首先,准备一个多行字符串作为输入:

String input = new StringBuilder().append("42\n")
  .append("It is a magic number.\n")
  .toString();

Scanner.nextLine()方法会消耗整行,包括行分隔符。但它只返回没有末尾换行符的文本,并将位置设置在下一行的开始。

// nextLine()
Scanner sc1 = new Scanner(input);
int num1 = Integer.parseInt(sc1.nextLine());
String nextLineText1 = sc1.nextLine();
assertEquals(42, num1);
assertEquals("It is a magic number.", nextLineText1);

然而,另一方面,Scanner.nextInt()会扫描输入流中的下一个整数标记,而不消耗其后的换行字符

// nextInt()
Scanner sc2 = new Scanner(input);
int num2 = sc2.nextInt();
assertEquals(42, num2);

// nextInt() leaves the newline charater (\n) behind
String nextLineText2 = sc2.nextLine();
assertEquals("", nextLineText2);

在这个测试中,我们先使用sc2.nextInt()获取了数字42,然后调用sc2.nextLine()。然后,我们可以看到,方法返回的是空字符串而不是“It is a magic number.”。这是因为nextInt()在“42"之后不会消费换行字符。

6. 总结

在这篇文章中,我们通过示例讨论了Integer.parseInt(Scanner.nextLine())Scanner.nextInt()之间的差异。以下是总结:

  • 对于格式不正确的输入,两种方法会抛出不同类型的异常。
  • Scanner.nextLine()会消耗Scanner中的换行字符,但返回的字符串不包含它。
  • Scanner.nextInt()不会消耗换行字符。
  • Scanner.nextInt()不会消费无法解析的标记。

如往常一样,这里展示的所有代码片段都在GitHub上可用。