# 来自命令行的 I / O.

> 原文： [https://docs.oracle.com/javase/tutorial/essential/io/cl.html](https://docs.oracle.com/javase/tutorial/essential/io/cl.html)

程序通常从命令行运行，并在命令行环境中与用户交互。 Java 平台以两种方式支持这种交互：通过标准流和通过控制台。

## 标准流

标准流是许多操作系统的一个特性。默认情况下，他们从键盘读取输入并将输出写入显示器。它们还支持文件和程序之间的 I / O，但该功能由命令行解释器控制，而不是程序。

Java 平台支持三种标准流：_ 标准输入 _，通过`System.in`访问; _ 标准输出 _，通过`System.out`访问;和 _ 标准错误 _，通过`System.err`访问。这些对象是自动定义的，不需要打开。标准输出和标准误差均用于输出;单独具有错误输出允许用户将常规输出转移到文件并仍然能够读取错误消息。有关更多信息，请参阅命令行解释程序的文档。

您可能希望标准流是字符流，但由于历史原因，它们是字节流。 `System.out`和`System.err`定义为 [`PrintStream`](https://docs.oracle.com/javase/8/docs/api/java/io/PrintStream.html) 对象。虽然它在技术上是字节流，但`PrintStream`利用内部字符流对象来模拟字符流的许多功能。

相比之下，`System.in`是一个没有字符流功能的字节流。要将标准输入用作字符流，请在`InputStreamReader`中包装`System.in`。

```
InputStreamReader cin = new InputStreamReader(System.in);

```

## 控制台

控制台是标准流的更高级替代方案。这是 [`Console`](https://docs.oracle.com/javase/8/docs/api/java/io/Console.html) 类型的单个预定义对象，具有 Standard Streams 提供的大部分功能，以及其他功能。控制台对于安全密码输入特别有用。 Console 对象还通过其`reader`和`writer`方法提供作为真实字符流的输入和输出流。

在程序可以使用控制台之前，它必须通过调用`System.console()`来尝试检索 Console 对象。如果 Console 对象可用，则此方法返回该对象。如果`System.console`返回`NULL`，则不允许控制台操作，因为操作系统不支持它们，或者因为程序是在非交互式环境中启动的。

Console 对象通过其`readPassword`方法支持安全密码输入。此方法有助于以两种方式保护密码输入。首先，它抑制回显，因此密码在用户屏幕上不可见。其次，`readPassword`返回一个字符数组，而不是`String`，因此密码可以被覆盖，一旦不再需要就将其从内存中删除。

[`Password`](examples/Password.java) 示例是用于更改用户密码的原型程序。它演示了几种`Console`方法。

```
import java.io.Console;
import java.util.Arrays;
import java.io.IOException;

public class Password {

    public static void main (String args[]) throws IOException {

        Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }

        String login = c.readLine("Enter your login: ");
        char [] oldPassword = c.readPassword("Enter your old password: ");

        if (verify(login, oldPassword)) {
            boolean noMatch;
            do {
                char [] newPassword1 = c.readPassword("Enter your new password: ");
                char [] newPassword2 = c.readPassword("Enter new password again: ");
                noMatch = ! Arrays.equals(newPassword1, newPassword2);
                if (noMatch) {
                    c.format("Passwords don't match. Try again.%n");
                } else {
                    change(login, newPassword1);
                    c.format("Password for %s changed.%n", login);
                }
                Arrays.fill(newPassword1, ' ');
                Arrays.fill(newPassword2, ' ');
            } while (noMatch);
        }

        Arrays.fill(oldPassword, ' ');
    }

    // Dummy change method.
    static boolean verify(String login, char[] password) {
        // This method always returns
        // true in this example.
        // Modify this method to verify
        // password according to your rules.
        return true;
    }

    // Dummy change method.
    static void change(String login, char[] password) {
        // Modify this method to change
        // password according to your rules.
    }
}

```

`Password`类遵循以下步骤：

1.  尝试检索 Console 对象。如果对象不可用，则中止。
2.  调用`Console.readLine`以提示并读取用户的登录名。
3.  调用`Console.readPassword`以提示并读取用户的现有密码。
4.  调用`verify`确认用户有权更改密码。 （在此示例中，`verify`是一个始终返回`true`的虚拟方法。）
5.  重复以下步骤，直到用户输入两次相同的密码：
    1.  调用`Console.readPassword`两次以提示并读取新密码。
    2.  如果用户两次输入相同的密码，请调用`change`进行更改。 （同样，`change`是一种虚拟方法。）
    3.  用空格覆盖两个密码。
6.  用空格覆盖旧密码。