- 前言
- 简介
- 第一章 对象的概念
- 第二章 安装 Java 和本书用例
- 第三章 万物皆对象
- 第四章 运算符
- 第五章 控制流
- 第六章 初始化和清理
- 第七章 封装
- 第八章 复用
- 第九章 多态
- 第十章 接口
- 第十一章 内部类
- 第十二章 集合
- 第十三章 函数式编程
- 第十四章 流式编程
- 第十五章 异常
- 第十六章 代码校验
- 第十七章 文件
- 第十八章 字符串
- 第十九章 类型信息
- 第二十章 泛型
- 第二十一章 数组
- 第二十二章 枚举
- 第二十三章 注解
- 第二十四章 并发编程
- 第二十五章 设计模式
- 附录:成为一名程序员
- 附录:静态语言类型检查
- 附录:集合主题
- 附录:数据压缩
- 附录:流式 IO
- 附录:文档注释
- 附录:并发底层原理
- 附录:新 IO
- 附录:对象序列化
- 附录:对象传递和返回
- 附录:编程指南
- 附录:标准 IO
- 附录:补充
- 附录:C++ 和 Java 的优良传统
- 附录:理解 equals 和 hashCode 方法
- 词汇表
附录:标准 IO
标准 I/O这个术语参考Unix中的概念,指程序所使用的单一信息流(这种思想在大多数操作系统中,也有相似形式的实现)。
程序的所有输入都可以来自于标准输入,其所有输出都可以流向标准输出,并且其所有错误信息均可以发送到标准错误。标准 I/O 的意义在于程序之间可以很容易地连接起来,一个程序的标准输出可以作为另一个程序的标准输入。这是一个非常强大的工具。
从标准输入中读取
遵循标准 I/O 模型,Java 提供了标准输入流 System.in
、标准输出流 System.out
和标准错误流 System.err
。在本书中,你已经了解到如何使用 System.out
将数据写到标准输出。 System.out
已经预先包装1成了 PrintStream
对象。标准错误流 System.err
也预先包装为 PrintStream
对象,但是标准输入流 System.in
是原生的没有经过包装的 InputStream
。这意味着尽管可以直接使用标准输出流 System.out
和标准错误流 System.err
,但是在读取 System.in
之前必须先对其进行包装。
我们通常一次一行地读取输入。为了实现这个功能,将 System.in
包装成 BufferedReader
来使用,这要求我们用 InputStreamReader
把 System.in
转换2成 Reader
。下面这个例子将键入的每一行显示出来:
// standardio/Echo.java
// How to read from standard input
import java.io.*;
import onjava.TimedAbort;
public class Echo {
public static void main(String[] args) {
TimedAbort abort = new TimedAbort(2);
new BufferedReader(
new InputStreamReader(System.in))
.lines()
.peek(ln -> abort.restart())
.forEach(System.out::println);
// Ctrl-Z or two seconds inactivity
// terminates the program
}
}
BufferedReader
提供了 lines()
方法,返回类型是 Stream<String>
。这显示出流模型的的灵活性:仅使用标准输入就能很好地工作。 peek()
方法重启 TimeAbort
,只要保证至少每隔两秒有输入就能够使程序保持开启状态。
将 System.out 转换成 PrintWriter
System.out
是一个 PrintStream
,而 PrintStream
是一个OutputStream
。 PrintWriter
有一个把 OutputStream
作为参数的构造器。因此,如果你需要的话,可以使用这个构造器把 System.out
转换成 PrintWriter
。
// standardio/ChangeSystemOut.java
// Turn System.out into a PrintWriter
import java.io.*;
public class ChangeSystemOut {
public static void main(String[] args) {
PrintWriter out =
new PrintWriter(System.out, true);
out.println("Hello, world");
}
}
输出结果:
Hello, world
要使用 PrintWriter
带有两个参数的构造器,并设置第二个参数为 true
,从而使能自动刷新到输出缓冲区的功能;否则,可能无法看到打印输出。
重定向标准 I/O
Java的 System
类提供了简单的 static
方法调用,从而能够重定向标准输入流、标准输出流和标准错误流:
- setIn(InputStream)
- setOut(PrintStream)
- setErr(PrintStream)
如果我们突然需要在显示器上创建大量的输出,而这些输出滚动的速度太快以至于无法阅读时,重定向输出就显得格外有用,可把输出内容重定向到文件中供后续查看。对于我们想重复测试特定的用户输入序列的命令行程序来说,重定向输入就很有价值。下例简单演示了这些方法的使用:
// standardio/Redirecting.java
// Demonstrates standard I/O redirection
import java.io.*;
public class Redirecting {
public static void main(String[] args) {
PrintStream console = System.out;
try (
BufferedInputStream in = new BufferedInputStream(
new FileInputStream("Redirecting.java"));
PrintStream out = new PrintStream(
new BufferedOutputStream(
new FileOutputStream("Redirecting.txt")))
) {
System.setIn(in);
System.setOut(out);
System.setErr(out);
new BufferedReader(
new InputStreamReader(System.in))
.lines()
.forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
System.setOut(console);
}
}
}
该程序将文件中内容载入到标准输入,并把标准输出和标准错误重定向到另一个文件。它在程序的开始保存了最初对 System.out
对象的引用,并且在程序结束时将系统输出恢复到了该对象上。
I/O重定向操作的是字节流而不是字符流,因此使用 InputStream
和 OutputStream
,而不是 Reader
和 Writer
。
执行控制
你经常需要在Java内部直接执行操作系统的程序,并控制这些程序的输入输出,Java类库提供了执行这些操作的类。
一项常见的任务是运行程序并将输出结果发送到控制台。本节包含了一个可以简化此任务的实用工具。
在使用这个工具时可能会产生两种类型的错误:导致异常的普通错误——对于这些错误我们只需要重新抛出一个 RuntimeException
即可,以及进程自身的执行过程中导致的错误——我们需要用单独的异常来报告这些错误:
// onjava/OSExecuteException.java
package onjava;
public class OSExecuteException extends RuntimeException {
public OSExecuteException(String why) {
super(why);
}
}
为了运行程序,我们需要传递给 OSExecute.command()
一个 String command
,我们可以在控制台键入同样的指令运行程序。该命令传递给 java.lang.ProcessBuilder
的构造器(需要将其作为 String
对象的序列),然后启动生成的 ProcessBuilder
对象。
// onjava/OSExecute.java
// Run an operating system command
// and send the output to the console
package onjava;
import java.io.*;
public class OSExecute {
public static void command(String command) {
boolean err = false;
try {
Process process = new ProcessBuilder(
command.split(" ")).start();
try (
BufferedReader results = new BufferedReader(
new InputStreamReader(
process.getInputStream()));
BufferedReader errors = new BufferedReader(
new InputStreamReader(
process.getErrorStream()))
) {
results.lines()
.forEach(System.out::println);
err = errors.lines()
.peek(System.err::println)
.count() > 0;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
if (err)
throw new OSExecuteException(
"Errors executing " + command);
}
}
为了捕获在程序执行时产生的标准输出流,我们可以调用 getInputStream()
。这是因为 InputStream
是我们可以从中读取信息的流。
这里这些行只是被打印了出来,但是你也可以从 command()
捕获和返回它们。
该程序的错误被发送到了标准错误流,可以调用 getErrorStream()
捕获。如果存在任何错误,它们都会被打印并且抛出 OSExcuteException
,以便调用程序处理这个问题。
下面是展示如何使用 OSExecute
的示例:
// standardio/OSExecuteDemo.java
// Demonstrates standard I/O redirection
// {javap -cp build/classes/main OSExecuteDemo}
import onjava.*;
public class OSExecuteDemo {}
这里使用 javap
反编译器(随JDK发布)来反编译程序,编译结果:
Compiled from "OSExecuteDemo.java"
public class OSExecuteDemo {
public OSExecuteDemo();
}
1. 译者注:这里用到了装饰器模式。 ↩
2. 译者注:这里用到了适配器模式。 ↩
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论