java.util.Scanner, hasNext...(); & next...(); Работа с памятью объекта

Что происходит?! Как работают методы класса Scanner next...(); и hasNext...(); с памятью?

Пример:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        
        boolean b = console.hasNextLine();
        String i = console.nextLine();
        String str = console.nextLine();
        
        System.out.println("Output:");
        System.out.println("i = " + i);
        System.out.println("str = " + str);
    }
}

При инициализации переменной b будет запущен ввод с консоли. Однако уже при инициализации переменной i он не потребуется. Метод nextInt(); почему-то просто берёт то же значение, что вводилось чуть раньше при вызове метода hasNextLine(); А вот уже при инициализации переменной str просит ввести новую строку.

Далее веселее: Меняем тип переменной i со String на int. При инициализации переменной b активируется ввод с консоли. Этот же ввод перенаправляется в метод nextInt(); при инициализации переменной i. А инициализация переменной str вообще пропускается! Не просят ввести очередную строку.

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);

        boolean b = console.hasNextLine();
        int i = console.nextInt();
        String str = console.nextLine();

        System.out.println("Output:");
        System.out.println("i = " + i);
        System.out.println("str = " + str);
    }
}

Очевидно это связано с тем, как работают эти методы с памятью объекта типа Scanner, в данном случае "console". Но где все эти фишки и случаи расписаны подробно - не знаю. Как и то, что вообще происходит во втором случае?! Пожалуйста, помогите!


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Первый пример

    boolean b = console.hasNextLine();

Как программе определить будет ли введена следующая строка? Никак. Входной файл может быть закрыт в любой момент. Поэтому программа ждёт ввода каких-нибудь данных.

Входной поток с консоли буферизуется по строкам. Вы заметили что строку можно редактировать и программа не видит ваших действий пока вы не нажмёте Enter?

Сочетание этих двух факторов заставляет программу ждать пока вы введёте первую строку.

    String i = console.nextLine();

Когда исполняется этот код, строка уже введена и доступна программе. Переменная i немедленно заполняется значением и мы движемся дальше.

    String str = console.nextLine();

А следующую строку пользователь ещё не вводил. Программа останавливается и ждёт пока вы отредактируете текст и нажмёте Enter. После этого двигаемся дальше.

Второй пример

    boolean b = console.hasNextLine();

Тут всё тоже самое что и в примере выше.

    int i = console.nextInt();

Здесь строка уже считана, начинаем её разбирать: пропускаем пробелы, считываем знак числа и цифры и останавливаемся на первом пробельном символе. Это может быть перевод строки, пробел или табуляция, но не только.

Число считывается из входного потока посимвольно. Когда найден неподходящий символ его "возвращают" в поток, так что когда вы продолжите чтение, первым вы увидите его.

Так или иначе перевод строки после чтения целого числа останется в потоке.

    String str = console.nextLine();

Читаем следующую строку. Но нет, не следующую: читаем остаток текущей строки. Если вы нажали Enter сразу после числа, вы получите пустую строку.

Вывод

Видно что nextInt и nextLine как-то плохо работают друг с другом. Это объяснимо: nextInt устроен так чтобы не отличать пробелы от переводов строк. nextLine - наоборот.

Решить проблему можно радикально: всегда читать целые строки. Если вам нужна не строка а целое, то извлекать его из строки после чтения:

String line = console.nextLine();
int i = Integer.parseInt(line, 10);

Вся механика хорошо описана в официальной документации по Scanner. Опущены детали относящиеся к буферизации: операционная система пересылает ввод целыми строками и т.п..

→ Ссылка