从底层逻辑,谈谈next()和nextLine()配合使用时,出现的“跳过输入”的现象


点击查看代码

package com.jia.scanner;

import java.util.Scanner;

public class Demo01 {
    public static void main(String[] args) {
        //创建一个扫描器对象,用于接受键盘数据
        Scanner sc = new Scanner(System.in);
        System.out.println("使用next方式接受:");
        //判断用户有没有输入字符串
        if (sc.hasNext()){
            //使用next方式接受
            String str = sc.next();
            System.out.println("输出的内容:" + str);
        }

        System.out.println("============================================");

        System.out.println("使用nextLine:");
        if (sc.hasNextLine()){
            String str = sc.nextLine();
            System.out.println("输出的内容:" + str);
        }
        //凡是属于IO流的类,如果不关闭会一直占用资源,要养成习惯用完就关掉
        sc.close();//为了节省资源
    }
}

这段代码,运行后输出的结果是:

会出现跳过用户输入的现象。但是,并不符合我的需求,我希望是在使用nextLine前,可以再次从键盘输入新的内容,再输出新的结果。
接下来,我们可以从输入缓冲区机制和next还有nextLine两种方法的底层处理逻辑进行分析,该现象产生的原因:

  1. 数据是在输入缓冲区作为“中间站”暂存
    计算机在处理键盘输入时,并不是让程序直接“实时”的读取每一个按键,这样效率太低了,而是通过输入缓冲区作为中间层:
    • 用户敲击键盘的字符会先存入缓冲区,直到用户按下回车键(\n),缓冲区才会通知程序“有数据可以读取”
    • 程序中的Scanner类本质就是从缓冲区中读取数据,而不是直接从键盘读取
  2. next()方法的底层处理逻辑:
    next()的核心逻辑是以空白字符为分隔符的非空白字符串读取,具体步骤是:
    • 跳过前导空白字符:自动忽略缓冲区中开头的所有空白字符(空格、指标符\t、换行符\n等)。
    • 读取有效的字符:从第一个非空白字符开始读取,持续读取直到遇到下一个空白字符(空格、换行等)。
    • 终止但是不消耗分隔符:当遇到空白字符时,next()会停止读取数据,但是这个空白字符不会被从缓冲区中移除,仍然留在缓冲区中。
  3. nextLine()方法的底层处理逻辑:
    nextLine()的核心逻辑是读取一整行内容,知道换行符为止,具体步骤是:
    • 从当前位置开始读取数据:不跳过任何字符,直接从缓冲区当前位置开始读取数据。
    • 以换行符为终止符:持续读取所有字符(包括空白字符),直到遇到\n(换行符)。
    • 消耗终止符:读取完成后,会将终止的\n(换行符)从缓冲区中移除(即“消耗”掉)。
      举例:
      用户输入“hello world\n”(\n是用户按回车键产生的换行符),计算机缓冲区的内容为“hello world\n”。
      next()执行后:
  • 读取到“hello” (非空白字符开始,知道空格停止);
  • 缓冲区中剩余“ world\n” (空格和后续字符未被消耗移除);
    nextLine()执行后:
  • 读取到“ world” (从当前位置,即空格开始,知道\n换行符为止);
  • 同时消耗掉\n(换行符),缓冲区变为空。
    综上所述,出现跳过输入现象的根本原因是:
  1. next()读取后,缓冲区中残留了未被消耗掉的空白字符(如空格)和换行符前的字符内容;
  2. 后续的nextLine()读取时,会直接从缓冲区这些残留的内容(从空白字符到\n),并消耗掉\n(换行符);
  3. 由于执行完next()读取都,缓冲区还有内容(从空白字符到\n)未被消耗,所以,nextLine()无需等待用户输入新内容,导致,看起来出现了“跳过输入现象”。
    当然,存在解决方法,只需在next()后额外调用一次nextLine()清空缓冲区,即可。
    本质是:
    让第一次nextLine()消耗掉next()残留的空白字符和\n,使缓冲区为空。这样,后续真正需要使用nextLine()就会等待用户输入新内容(因为缓冲区已经被清空,必须等待新的\n触发)。

点击查看代码

package com.jia.scanner;

import java.util.Scanner;

public class Demo01 {
    public static void main(String[] args) {
        //创建一个扫描器对象,用于接受键盘数据
        Scanner sc = new Scanner(System.in);
        System.out.println("使用next方式接受:");
        //判断用户有没有输入字符串
        if (sc.hasNext()){
            //使用next方式接受
            String str = sc.next();
            sc.nextLine();//紧跟着,使用nextLine释放空格,程序才会接着让用户输入
            System.out.println("输出的内容:" + str);
        }

        System.out.println("============================================");

        System.out.println("使用nextLine:");
        if (sc.hasNextLine()){
            String str = sc.nextLine();
            System.out.println("输出的内容:" + str);
        }
        //凡是属于IO流的类,如果不关闭会一直占用资源,要养成习惯用完就关掉
        sc.close();//为了节省资源
    }
}

该代码运行结果如下:

符合我的需求!