两个版本的 Scanner 之间有什么区别?

What are the differences between two versions of Scanner?

这些从控制台读取输入的方式有什么区别?

Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));

Scanner in = new Scanner(new BufferedInputStream(System.in));

以及优缺点是什么?

我认为回答这些问题的最好方法是检查实际实现的源代码。您可以从这里下载源代码:http://download.java.net/openjdk/jdk8/ 或只是 Google 具体 类:"java.util.Scanner source".

在这种情况下匹配源:

public Scanner(InputStream source) {
   this(new InputStreamReader(source), WHITESPACE_PATTERN);
}

您可以使用这段代码实现同样的效果:

Scanner in = new Scanner(new BufferedReader(new InputStreamReader(new BufferedInputStream(System.in))));

Scanner 构造函数

就目前的情况而言,没有太大区别。

您使用的 Scanner 的第一个构造函数 Scanner(Readable) 将一个对象作为参数,该对象表示 字符 的传入序列,可以是使用 CharBuffer.

阅读

您使用的 Scanner 的第二个构造函数 Scanner(InputStream) 将一个对象作为参数,该对象表示 字节 .[=38 的传入序列=]

需要强调的是字节不是字符。根据字符编码,字符可能由不同的字节序列表示,并且每个字符可能跨越多个字节。

在内部,第二个构造函数立即用 InputStreamReader 包装参数 - 这是一个 Readable 提供字符数据,因此它几乎与使用第一个构造函数相同。

BufferedInputStream 对比 BufferedReader

所以剩下的区别是

new BufferedReader(new InputStreamReader(System.in))

new InputStreamReader(new BufferedInputStream(System.in));

它们读取数据的方式略有不同。

每次需要输入时,第一个链将填充 8192 个字符的缓冲区,从底层 reader 获取每个字符,后者将从System.in.

字节

每次需要输入时,第二条链将填满 8192 字节 的缓冲区。因此,当包装 reader 需要下一个字符时,假设该字符在输入中由两个字节表示,而当前缓冲区中只有该字符的一个字节。第二个字节将需要再次填充缓冲区。

我没有经验数据,但我认为鉴于 Scanner 本身每次需要数据时都会填充一个 CharBuffer,所以上述细微差别将是微不足道的。事实上,我相信您可以安全地放弃使用 BufferedReaderBufferedInputStream,而只需将 InputStreamReaderSystem.in 直接提供给 Scanner,这将花费注意缓冲。

指定输入的字符集

如果您需要使用特定字符集进行输入,有所不同。例如,如果您想确保传入的字节流被解释为 UTF-8,您可以使用:

Scanner in = new Scanner(new InputStreamReader(System.in,StandardCharsets.UTF_8));

否则,输入字节将被解释为您的默认字符集,该字符集不一定是 UTF-8

Scanner 的另一个构造函数也允许使用普通 InputStream:

Scanner in = new Scanner(System.in, "UTF-8");

这也将立即用 InputStreamReader 换行 System.in,所以差别不大。