一个扫描器应该只示例化一次吗?如果是这样,为什么?

g0czyy6m  于 2021-06-27  发布在  Java
关注(0)|答案(4)|浏览(418)

我知道我在这里有点冒险,但我似乎不明白为什么不能创建两次scanner类的示例。我再加一个例子以防万一。

import java.util.Scanner;

public class Nope
{
    public static void main(String[] args)
    {
        System.out.println("What's your name?");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();

        System.out.println("Welcome " + name + "!");
        scanner.close();

        // Now 
        System.out.println("where you do live?");
        Scanner sc = new Scanner(System.in);
        String country = sc.nextLine();

        System.out.println("That's a lovely place");
        sc.close();

    }
}

我得到一个运行时错误,看起来像这样

What's your name?
Kate
Welcome Kate!
Exception in thread "main" where you do live?
java.util.NoSuchElementException: No line found
    at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
    at Nope.main(Nope.java:17)

我知道再次创建同一类的新对象,鼓励冗余是没有意义的。但我想如果我知道原因我会清醒的,你不也这么认为吗?
这台机器所说的“java.util.nosuchelementexception:no line found”是什么意思?人们说扫描仪是不可克隆的。
ps:我故意关闭了我的第一个扫描仪,并创建了一个新的对象,只是为了理解这个问题。

4xrmg8kj

4xrmg8kj1#

它很简单,每个输入只需要创建一个扫描仪。扫描仪使用nextline()方法逐行读取。最后检查条件hasnext(),以便找出参数。
试试这个

import java.util.Scanner;

public class Nope{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("What's your name?");
        String name = scanner.nextLine();

        System.out.println("Welcome " + name + "!");
        // Now
        System.out.println("where you do live?");
        String country = scanner.nextLine();

        System.out.println(country +" Is a lovely place");
        if(!scanner.hasNext()){
        scanner.close();
        }
    }
}

输出类

What's your name?
xyz
Welcome xyz!
where you do live?
rjk
rjk Is a lovely place
eeq64g8w

eeq64g8w2#

这个答案集中在 close() 操作以及为什么没有选择从 System.in 如果一个扫描器示例之前关闭了它,因为上面的答案已经给出了正确的信息。只是好奇而已。
扫描仪
当扫描仪关闭时,如果输入源实现可关闭接口,它将关闭输入源。如果没有外部同步,扫描仪对于多线程使用是不安全的。
您应该为每个要从中读取的源创建一个scanner示例。
如果必须共享同一个示例,那么应该实现同步机制,因为它不是线程安全的。
其他答案已经指出, close() 是个“危险”的行动。

system.in close()

让我们假设一下 System.in 已指定为源。
这是最接近的方法 InputStreamReader ```
public void close() throws IOException
{
synchronized (lock)
{
// Makes sure all intermediate data is released by the decoder.
if (decoder != null)
decoder.reset();
if (in != null)
in.close();
in = null;
isDone = true;
decoder = null;
}
}

引用的变量 `System.in` 那个叫 `in` .
对该对象执行两个操作 `InputStream` (除空检查外):
1. `in.close()` 这根本不起作用: `System.in` 的类( `InputStream` )只留下继承的 `close()` 方法(从 `Closeable` )

/**

  • Closes this stream. Concrete implementations of this class should free
  • any resources during close. This implementation does nothing.

/
public void close() throws IOException {
/
empty */
}

甚至连javadocshide都不知道真相:
inputstream的close方法不起任何作用。
2. `in = null` 这就是为什么你不能再次阅读的真正原因 `System.in` . 如果将其设置为null,则无法使用新的 `Scanner` .
但是。。。为什么它会抛出一个 `NoSuchElementException` 而不是 `NullPointerException` ?
初始化 `Scanner` 在创建它的 `Reader` 示例。这是因为 `InputStream` 被 Package 成一个新的 `BufferedInputStream` . 所以 `lock` 对象在初始化扫描仪的 `Reader` :

public InputStreamReader(InputStream in)
{
super(in);
this.in = in;
...
}

.

protected Reader(Object lock)
{
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}

你可以创建第二个 `Scanner` 示例来自 `System.in` 没有任何异常被抛出;作为 `InputStream` 被 Package 成一个新的 `BufferedInputStream` 例如 `lock` 对象不为null并通过筛选器。但是内心 `InputStream` ,  `System.in` ,实际上是空的,从上一节中设置为空的那一刻起 `close()` 操作:
![](https://i.stack.imgur.com/X6xnt.png)
这就是 `lock` 对象的第二次初始化 `Scanner` 为了 `System.in` . 扫描仪仍然不知道会有什么不好的地方,因为它的初始化是成功的(由于 `BufferedInputStream` )仍然相信 `InputStream` 是有效的。
但在第一次尝试从 `System.in` ,发生这种情况:

public String nextLine() {
if (hasNextPattern == linePattern())
return getCachedResult();
clearCaches();

String result = findWithinHorizon(linePattern, 0);
if (result == null) /* result is null, as there's no source */
   throw new NoSuchElementException("No line found");

 (...)

}

那一刻 `Scanner` 最后注意到有些事情进展不顺利。结果 `findWithinHorizon` 将返回null,因为从何处找不到源。
由于先前的设置 `System.in` 在 `close()` 操作时,尝试从第二个 `Scanner` 示例: `NoSuchElementException` .
c7rzv4ha

c7rzv4ha3#

实际上,这里有两件不同的事情。
你应该创建一个 Scanner 每个输入源。例如,一个 Scanner 对于每个不同的输入文件,一个用于 System.in ,每个不同的套接字输入流一个。
原因是(正如chrylis指出的)不同的 Scanner 预读扫描仪的输入源。如果操作未使用字符,则不会将其放回输入源。相反,它们是由 Scanner ,并为下一个 Scanner 要使用的操作。因此,如果您有两个扫描器试图从同一输入源读取数据,其中一个可能会窃取另一个的输入。
这就是为什么要开多家公司的真正原因 Scanner 上的对象 System.in 很糟糕。不是你提出的“冗余”论点。有一点冗余从根本上说没有什么错。。。尤其是如果它简化了应用程序。但是扫描器争夺输入可能会导致意外的行为/错误。
第二个问题是当你 close()Scanner 这也会关闭输入源。
对你来说,这意味着你要关门了 System.in . 然后你创造了第二个 Scanner 阅读(现已关闭) System.in .
当你试图给我们一个 Scanner 从一个封闭的 System.in ,这将导致 NoSuchElementException .
如果你没打电话 close() 一开始 Scanner ,您的代码可能已经工作,但这将取决于您在第一个 Scanner .
人们在说 Scanner 不可克隆。
他们是对的。

pkbketx9

pkbketx94#

你应该只创建一个 Scanner 每个输入流。除此之外,扫描仪会提前读取,因此会消耗比实际返回的更多的输入(这就是它如何知道,例如,输入 hasNextInt() 以此类推。)
如果您有多个输入流(例如处理多个文件),那么创建多个扫描器是完全明智的,但是 System.in 应该只有一个扫描仪使用它。

相关问题