Java с использованием сканера с попыткой-с-ресурсами

У меня есть две версии кода Java, которые получают пользовательский ввод до тех пор, пока пользователь не введет «q» версии 1:

public class Test {
    public static void main(String[] args) {
        String input = "";
        while (!input.equals("q")) {
            Scanner scanner = new Scanner(System.in);
            System.out.print("Input: ");
            input = scanner.nextLine();
            System.out.println("Input was: " + input);
        }
    }
}

Версия 2:

public class Test {
    public static void main(String[] args) {
        String input = "";
        while (!input.equals("q")) {
            try(Scanner scanner = new Scanner(System.in)){
                System.out.print("Input: ");
                input = scanner.nextLine();
                System.out.println("Input was: " + input);
            }
        }
    }
}

Версия 1 работает так, как ожидалось, но версия 2 не работает должным образом. То есть после первого чтения пользовательского ввода выдает ошибку

Input: 12
Input was: 12Exception in thread "main" 
Input: java.util.NoSuchElementException: No line found
    at java.util.Scanner.nextLine(Scanner.java:1540)
    at RealEstateCompany.main(RealEstateCompany.java:115)

Я предполагаю, что поскольку версия 2 использует попытку с ресурсом, поэтому он закрывает сканер после использования, и это вызывает ошибку?

Спасибо за вашу помощь заранее!

[Обновление] Версия 3:

public class Test {
    public static void main(String[] args) {
        String input = "";
        try(Scanner scanner = new Scanner(System.in)){
            while (!input.equals("q")) {
                System.out.print("Input: ");
                input = scanner.nextLine();
                System.out.println("Input was: " + input);
            }
        }
    }
}

Версия 3 работает. Однако, почему версия 3 в порядке, а версия 2 не в порядке?


person ksn    schedule 01.12.2017    source источник
comment
try-with автоматически закрывает базовый Scanner, который автоматически закрывает базовый поток, то есть System.in. После этого вы больше не сможете получать какие-либо данные от пользователя.   -  person QBrute    schedule 01.12.2017
comment
Вы пытались сохранить попытку (сканер сканера = новый сканер (System.in)) вне цикла while?   -  person Raju Sharma    schedule 01.12.2017
comment
@RajuSharma Спасибо за совет. Да, это решило мою проблему, но почему это решает проблему?   -  person ksn    schedule 01.12.2017
comment
Версия 3 работает, потому что когда вы входите в цикл while, вы все еще находитесь внутри блока try. Ресурс закрывается только тогда, когда вы покидаете try, что и произошло во втором примере после одной итерации.   -  person QBrute    schedule 01.12.2017
comment
@QBrute Спасибо за ответ! Я новичок в Java, так что не могли бы вы дать мне немного больше деталей?   -  person ksn    schedule 01.12.2017
comment
@QBrute, так что в версии 2 этой строки попытка (сканер сканера = новый сканер (System.in)) не создает экземпляр сканера, потому что он закрыт в первой итерации?   -  person ksn    schedule 01.12.2017
comment
@ksn добавил дополнительную информацию в качестве ответа   -  person QBrute    schedule 01.12.2017


Ответы (3)


Добавление дополнительных деталей к моим комментариям

Блок try-with определяется следующим образом:

try(...) {
   ...
}

где аргумент в скобках должен быть экземпляром java.lang.AutoCloseable< /а>. Примером может служить класс java.io.InputStream, который также класс для System.in.

try-with пытается автоматически закрыть предоставленный ему ресурс, как только блок будет оставлен. В зависимости от используемого ресурса он также закрывает все собственные дочерние ресурсы.

Взяв ваш пример, у вас есть try(Scanner scanner = new Scanner(System.in)), который использует Scanner в качестве ресурса. Сам сканер использует System.in в качестве ресурса. Как только блок try покинут (при достижении }), он попытается закрыть свои ресурсы, то есть экземпляр Scanner. Этот экземпляр также пытается закрыть свой ресурс, файл System.in.

Как только System.in закрывается, вы больше не можете получать какие-либо данные из консоли (по крайней мере, с дополнительной работой, я думаю...).

Конкретно, во втором примере:

while (!input.equals("q")) {
    try(Scanner scanner = new Scanner(System.in)){
            ...
    }  // <--- The block is left, scanner is closed, System.in is closed
} // <-- start a new iteration

Здесь после всего лишь одной итерации System.in закрывается. Конечно, вы создаете новый Scanner на следующей итерации, но System.in остается закрытым, поэтому в этом случае вы получаете исключение.

Ваш третий пример:

try(Scanner scanner = new Scanner(System.in)){
    while (!input.equals("q")) {
        ...
    } // <-- start a new iteration, while still in the same try block
} // <-- only after the while, your resources are closed

Здесь вы зацикливаете свой while, все еще находясь внутри try. Таким образом, ни один ресурс не будет закрыт, пока вы не покинете while и try. То есть Scanner остается нетронутым, а вместе с ним и System.in. Это позволяет вам продолжать чтение с консоли, пока вы не закончите цикл.

person QBrute    schedule 01.12.2017
comment
Большое спасибо за максимально понятное объяснение. Теперь я чувствую себя намного лучше, используя try-with-resources! - person ksn; 01.12.2017

Попробуй это:

   String input = "";
   try (Scanner scanner = new Scanner(System.in)) {
       while (!input.equals("q")) {
           System.out.print("Input: ");
           input = scanner.nextLine();
           System.out.println("Input was: " + input);
       }
   }

Вы можете использовать каждый класс, который реализует Closeable или AutoCloseable в попытке с ресурсами. Когда код достигает конца вызова попытки, он вызывает функцию close() класса Scanner в нашем примере.

person Gal Shaboodi    schedule 01.12.2017
comment
Разве это не версия 3 в посте ОП? - person achAmháin; 01.12.2017
comment
@Gal Спасибо за ваш ответ и объяснение. Единственное, чего я сейчас не понимаю, так это почему версия 2 не работает. Я понял, что в конце try close() вызывается и сканер закрывается, но во второй итерации в try(Scanner scan = new...) он воссоздает новый экземпляр сканера, поэтому я думаю, что он должен иметь возможность читать ввод еще раз. - person ksn; 01.12.2017
comment
Проголосовали против, потому что ответ не отвечает на вопрос ОП. - person Leroy; 01.12.2017

я запускаю несколько тестов и добавляю блок catch в ваш код. вот код

public static void main(String[] args) {
    String input = "";
    while (!input.equals("q")) {
        try(Scanner scanner = new Scanner(System.in)){
            System.out.print("Input: ");
            input = scanner.nextLine();
            System.out.println("Input was: " + input);
        }
        catch (Exception e) {
            e.printStackTrace();
        }            
    }
}

при добавлении блока catch есть 2 вида результатов 1, вводится только q, работает как положено 2, вводится любая другая строка, исключение

Input: java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:11)

при добавлении блока catch мы увидим, что программа не остановится из-за цикла while

вот еще один простой тест

public class Test {
public static void main(String[] args) {
    String input = "";
    Scanner scanner = new Scanner(System.in);
    System.out.println("inout--1---");
    input = scanner.nextLine();
    scanner.close();

    Scanner scanner2 = new Scanner(System.in);
    System.out.println("inout--2---");
    input = scanner2.nextLine();
    scanner2.close();

}

}

и это такое же исключение

inout--1---
11
inout--2---
Exception in thread "main" java.util.NoSuchElementException: No line  found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:15)

вот мое мнение. в конце первого запуска try()block закроет ресурс, который находится в блоке, это означает, что мы закрываем system.in system.in является объектом inputSteam, а system.in является окончательным и статическим, мы не можем откройте его снова, как «новый сканер (System.in)»

person hl.Pan    schedule 01.12.2017