3.10 Scanner类的bug

在有了一些使用 Scanner 的经验后,有必要提醒你一下,它存在一个出人意料的行为。下面的代码片段让用户输入姓名和年龄:

  1. System.out.print("What is your name? ");
  2. name = in.nextLine();
  3. System.out.print("What is your age? ");
  4. age = in.nextInt();
  5. System.out.printf("Hello %s, age %d\n", name, age);

其输出可能类似于以下这样:

  1. Hello Grace Hopper, age 45

先读取 String 再读取 int 时,一切都正常,但如果先读取 int 再读取 String,将发生怪异的事情。

  1. System.out.print("What is your age? ");
  2. age = in.nextInt();
  3. System.out.print("What is your name? ");
  4. name = in.nextLine();
  5. System.out.printf("Hello %s, age %d\n", name, age);

如果尝试运行上述示例代码,你将发现它根本不允许输入姓名,而在你输入年龄后会直接显示输出:

  1. What is your name? Hello , age 45

要想明白其中的原因,你必须知道的是,Scanner 并不像我们那样将输入视为多行;相反,它获得的是一个如图 3-3 所示的“字符流”。

{%}

图 3-3:Scanner 眼中的字符流

其中的箭头指向 Scanner 将读取的下一个字符。调用 nextInt 时,它会不断读取字符,直到遇到非数字字符。图 3-4 显示了调用 nextInt 后流的状态。

{%}

图 3-4:调用 nextInt 后的字符流

此时,nextInt 返回 45。接下来,程序显示提示 What is your name?,并调用 nextLine;这个方法会不断读取字符,直到遇到换行符。然而,由于下一个字符就是换行符,因此 nextLine 返回一个空字符串("")。

为解决这个问题,需要在 nextInt 后面多调用一次 nextLine

  1. System.out.print("What is your age? ");
  2. age = in.nextInt();
  3. in.nextLine(); // 读取换行符
  4. System.out.print("What is your name? ");
  5. name = in.nextLine();
  6. System.out.printf("Hello %s, age %d\n", name, age);

读取独占一行的 intdouble 值时,经常需要使用技巧:先读取数字,再读取当前行余下的全部内容(仅仅是一个换行符)。