Проверка данных и сканеры в Java

У меня есть вопрос относительно проверки данных и сканеров. Следующий фрагмент кода проверяет пользовательский ввод. Все, кроме целого числа, не разрешено, и пользователю предлагается повторно ввести значение. Мой вопрос заключается в том, что код работает, только если сканер объявлен в цикле while. Программа выполняется бесконечно, если сканер объявлен снаружи. Почему? Спасибо.

int UserInp;
    boolean dataType=false;
    while(dataType==false)
    {
        Scanner sc=new Scanner(System.in);
        try
        {

    System.out.print("\nEnter a number: ");
    UserInp=sc.nextInt();

    dataType=true;
        }
        catch(Exception JavaInputMismatch)
        {

            System.out.println("Option not available.Try again.");

        }

    }

person 12dec1990    schedule 11.06.2015    source источник
comment
Нет, это не так. У меня все работает нормально. Вы уверены, что единственное изменение, которое вы делаете, это перемещение Scanner sc = new Scanner(System.in); перед циклом?   -  person Purag    schedule 11.06.2015
comment
Мне удалось воспроизвести его проблему, и я исследую ее сейчас.   -  person Tim Biegeleisen    schedule 11.06.2015
comment
@TimBiegeleisen Как ты заставил его вести себя так?   -  person Purag    schedule 11.06.2015
comment
Я запускаю его код как консольное приложение от IntelliJ. Я уверен, что это какой-то артефакт... просто пытаюсь выяснить, что именно.   -  person Tim Biegeleisen    schedule 11.06.2015
comment
О, кстати, я тоже повторил. Это происходит только при неверном вводе, хех.   -  person Purag    schedule 11.06.2015
comment
С другой стороны, хотя while(dataType==false) является технически правильным синтаксисом, считается более элегантным выразить его как while(!dataType). Кроме того, вы можете использовать цикл do-while для случаев, когда вы хотите принудительно выполнить хотя бы одну итерацию.   -  person hfontanez    schedule 11.06.2015
comment
О, я этого не знал. Я всего лишь новичок, поэтому я довольно плохо разбираюсь в правильных методах программирования.   -  person 12dec1990    schedule 11.06.2015
comment
Я думаю, что иногда можно использовать == false. Это намного понятнее, и если вы правильно назовете свои переменные (например, вместо dataType, если бы он был intEntered), это будет читаться как настоящий английский.   -  person Purag    schedule 11.06.2015
comment
@Purag, как я уже говорил ранее, это технически правильный синтаксис. Однако мировое сообщество программистов предпочитает не повторяться, оценивая логическое значение с помощью оператора ==.   -  person hfontanez    schedule 11.06.2015
comment
Я думаю, что привык видеть его в качестве наставника для вводного класса Java. :П   -  person Purag    schedule 11.06.2015


Ответы (3)


Интересная проблема!

Что происходит, так это то, что сканер пытается преобразовать нецелое число в целое и понимает, что не может, поэтому выдает исключение InputMismatchException. Однако он продвигается дальше маркера только в том случае, если перевод был успешным.

Это означает, что недопустимая строка все еще находится во входном буфере, и она будет терпеть неудачу при переводе каждый раз, когда вы зацикливаетесь и пытаетесь вызвать nextInt(). Вы никогда не устанавливаете dataType в true, и поэтому вы зацикливаетесь бесконечно.

Чтобы увидеть это в действии, вы можете взять произвольное содержимое в блоке catch и распечатать его:

catch(Exception JavaInputMismatch){
    System.out.println( sc.next() );
    System.out.println("Option not available.Try again.");
}

Действительно, после неверного ввода мы получаем следующее:

Enter a number: hello
hello
Option not available.Try again.

Enter a number:

И мы не зацикливаемся бесконечно. Это связано с тем, что вызов next() захватил значение из входного буфера и переместил указатель сканера в этот буфер к следующему слоту, который сейчас пуст. Так что nextInt() будет ждать ввода в этом случае.

О, и причина, по которой он отлично работает, если вы инициализируете в цикле, заключается в том, что сканер всегда начинает считывать ввод заново; сканеры не разделяют состояние между экземплярами, поэтому «привет», который был в буфере для предыдущей итерации, отсутствует в буфере для следующей из-за повторной инициализации.

Технически он все еще находится в стандартном входном буфере, но указатель сканера на этот буфер находится за пределами недопустимой строки, поскольку он начнет считывать любой новый ввод, а не существующий ввод.

person Purag    schedule 11.06.2015

Чтобы добавить к ответу Purag, вы также можете использовать nextLine() для продвижения сканера за текущую строку.

Таким образом, ваш блок catch будет выглядеть так:

catch(Exception JavaInputMismatch)
    {
        System.out.println("Option not available.Try again.");
        sc.nextLine();
    }
person Srini    schedule 11.06.2015

Каверзный вопрос.

Вы можете получить это!

Ответ прост. Scanner object сохраняется до конца выполнения, поскольку он объявлен вне while loop. Посмотрите эту проблему на уровне памяти.

Объект сканера остается в живых, поэтому при входе в цикл в следующий раз значение (строковое значение) все еще будет в объекте сканера, и он не прослушивает клавиатуру, поскольку исключение уже выдано. Таким образом, цикл продолжается.

Примечание. Метод next() в классе Scanner будет принимать все типы ввода с клавиатуры, но не остальные методы, такие как nextInt(), nextFloat() и т. д.,

person SASIKUMAR S    schedule 11.06.2015