使用System.setOut(java.io.PrintStream)和System.setErr(java.io.PrintStream)可以将控制台的输出流重定向到其它的单个输出流比如文件输出流中。
但若要将控制台的输出流重定向到多个输出流(包括控制台),这时,可以通过代理的方式来实现,在Java里一般有两种方式来实现代理,一是继承组合,一是动态代理
1.继承原先的PrintStream,并维持一个多个输出流的集合,当调用该类的任意方法时,调用输出流集合里输出流的方法,伪代码如下:
public ManyPrintStream extends PrintStream {
private List<PrintStream> streamList = new ArrayList<PrintStream>();
................
public void method(....) {
for (int i = 0; i < streamList.size(); i++) {
PrintStream ps = streamList.get(i);
ps.method(....);
}
}
}
这种方法可行,但由于PrintStream的方法较多,实现比较麻烦。
2.一种更为简便的方法是动态代理,这种代理的原理在生成代理动态拦截对被代理类的方法的调用,我们可以使用java.lang.reflect.Proxy 类来生成动态代理(JDK动态代理),但是该代理只能基于接口(Interface)生成代理,而java.io.PrintStream是没有继承口的具体类,所以我们无法使用JDK动态代理来实现。但我们可以采用第三方提供的基于类生成代理的方法,CGLIB就是一种基于类(class)生成代理的开源库,用CGLIB生成代理要求被代理的类具有默认的无参构造函数(Constructor),而PrintStrem不符合些要求,但我们可能通过继承来达到些目的:
使用CGLIB需要的jar包:cglib-2.1_3.jar,asm-1.5.3.jar,asm-attrs-1.5.3.jar
或cglib-nodep-2.1_3.jar
二者选其一即可,两者都选会导致jar包冲突
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
//被代理的类
public class PseudoPrintStream extends PrintStream {
public PseudoPrintStream() {
super(new OutputStream() {//匿名OutputStream
public void write(int b) throws IOException {
//不用实现,只是为了比父类增加一个无参构造函数
}
});
}
}
下面是代理的实现代码:
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//处理代理被调用的类
public class StreamHandler implements MethodInterceptor{
//输出流链表
private List<PrintStream> streamList = new ArrayList<PrintStream>();
//代理处理类生效,不允许再添加输出流标志
private boolean validate = false;
/**添加输出流*/
public synchronized void addStream(PrintStream ps) {
if (validate) {
throw new IllegalStateException();
}
streamList.add(ps);
}
/**代理生效*/
public synchronized void validate() {
validate = true;
}
/**查询代理是否生效*/
public synchronized boolean getValidate() {
return validate;
}
/**拦截被代理的方法并处理*/
public Object intercept(Object object,
Method method,
Object[] args,
MethodProxy proxy)
throws Throwable {
//如果是PseudoPrintStream 的close方法被调用,则调用PseudoPrintStream 的close
//方法关闭其匿名输出流
if ("close".equals(method.getName())) {
proxy.invokeSuper(object, args);
}
Object object;
for (int i = 0; i < streamList.size(); i++) {
PrintStream ps = streamList.get(i);
object = method.invoke(ps, args);
}
return obejct;//返回链表最后一个输出流调用的结果
}
/**生成代理的方法*/
public static PrintStream proxyFor(StreamHandler handler) {
if (!handler.getValidate()) throw new RuntimeException("Handler not validate");
Enhancer enhancer = new Enhancer();
//被代理的ClassLoader
enhancer.setClassLoader(PseudoPrintStream.class.getClassLoader());
//被代理的Class
enhancer.setSuperclass(PseudoPrintStream.class);
//代理的方法被调用时,处理该调用的MethodInterceptor接口实现
enhancer.setCallback(handler);
//生成代理
PrintStream proxy= (PseudoPrintStream) enhancer.create();
//返回生成的代理
return proxy;
}
}
测试类
import java.io.FileOutputStream;
import java.io.PrintStream;
public class TestManyStream {
public static void main(String[] args) throws Exception{
//文件输出流1
FileOutputStream fo1 = new FileOutputStream("E:\\file1.txt", true);
//文件输出流2
FileOutputStream fo2 = new FileOutputStream("E:\\file2.txt", true);
//PrintStream1
PrintStream ps1 = new PrintStream(fo1);
//PrintStream1
PrintStream ps2 = new PrintStream(fo2);
//代理被调用时的处理类
StreamHandler sHandler = new StreamHandler();
sHandler.addStream(ps1);
sHandler.addStream(ps2);
sHandler.addStream(System.out);
sHandler.validate();
PrintStream streamProxy = StreamHandler.proxyFor(sHandler);
//系统输出流重定向到代理
System.setOut(streamProxy);
System.setErr(streamProxy);
System.out.println("All stream print this out!");
try {
throw new Exception("An Exception Occured!");
} catch (Exception e) {
e.printStackTrace();
}
streamProxy.close();
}
}
运行该测试类会发现控制台,E:\file1.txt,E:\file2.txt输出同样的内容
3.还可以通过继承OutputStream来实现,参照FilterOutputStream。
示例代码:不考虑一致性,错误处理策略等
public class MultiStream extends OutputStream {
private List<OutputStream> streamList = new ArrayList<OutputStream>();
//其他构造方法省略
public MultiStream(OutputStream ...outputStreams) {
streamList.addAll(Arrays.asList(outputStreams));
}
public void close() throws IOException {
IOException e = null;
//依次调用多个输出流的close方法
for (OutputStream o:streamList) {
try {
o.close();
} catch (IOException ioE) {
e = ioE;
}
}
if (e != null) {
throw e;
}
}
public void flush() throws IOException {
...
}
public void write(byte[] b, int off, int len) throws IOException {
IOException e = null;
for (OutputStream o:streamList) {
try {
o.write(b, off, len);
} catch (IOException ioE) {
e = ioE;
}
}
if (e != null) {
throw e;
}
}
public void write(byte[] b) throws IOException {
...
}
public void write(int b) throws IOException {
...
}
}
测试类
import java.io.FileOutputStream;
import java.io.PrintStream;
public class TestMultiStream {
public static void main(String[] args) throws Exception {
//文件输出流1
FileOutputStream fo1 = new FileOutputStream("E:\\file1.txt", true);
//文件输出流2
FileOutputStream fo2 = new FileOutputStream("E:\\file2.txt", true);
//PrintStream
MultiStream mStream = new MultiStream(fo1, fo2);
//MultiStream mStream = new MultiStream(fo1, fo2,System.out);
PrintStream ps = new PrintStream(mStream);
//系统输出流重定向
System.setOut(ps);
System.out.println("All stream print this out!");
ps.close();
}
}
运行该测试类会发现控制台,E:\file1.txt,E:\file2.txt输出同样的内容
分享到:
相关推荐
`LoopedStreams.java`可能是一个用于循环处理多个流的类,它可能包含了一个循环结构,用于同时读取并处理`System.out`和`System.err`。这样的设计可以确保不丢失任何输出信息,尤其是在并发环境下。 `...
- **SequenceInputStream**: 可以合并多个输入流。 **3.2 字符流** - **Reader**: 所有字符输入流的父类。 - **Writer**: 所有字符输出流的父类。 字符流与字节流的主要区别在于它们的基本单位不同。字符流以`...
这样,所有原本输出到控制台的信息都会被发送到管道流中,然后由另一个线程或进程处理。 #### 注意事项 1. **缓冲区大小**:`PipedInputStream`有一个默认的缓冲区大小为1024字节。当向`PipedOutputStream`写入的...
Java提供了一种机制,可以通过重定向输出流来将程序的输出信息记录到文件中,而不是仅仅显示在控制台上。以下是对这一主题的详细解释。 首先,Java的`System`类中包含三个标准流:`System.out`、`System.err`和`...
### Python捕获控制台输出流的方法详解 在Python编程中,有时我们需要捕获程序或外部命令执行时产生的控制台输出信息。这些信息可能是错误提示、警告或是其他有用的反馈,对于调试和日志记录非常关键。本文将详细...
对于标准输入/输出的重定向,Java允许程序捕获和改变系统的标准输入、输出和错误流,这在处理命令行参数和控制台交互时非常有用。 网络编程和多线程也是Java的重要部分,但不在输入输出的主题内。网络编程涉及...
在Java编程语言中,I/O(输入/输出)是程序与外部世界交互的重要部分,而标准输入输出流(System.in, System.out, System.err)是Java内置的预定义流,用于处理程序与操作系统之间的基本输入输出操作。这篇博客将深入...
如果想要同时保留控制台输出,可以创建一个` TeeOutputStream`,这是一个特殊的输出流,它会将数据同时写入两个或多个输出流。`TeeOutputStream`并不包含在Java标准库中,但可以通过第三方库如Apache Commons IO...
2. 重新打开标准输入/输出/错误流:通常重定向到 `/dev/null`,因为守护进程不需要与终端交互。 3. 设置父进程为init进程:在Unix系统中,这通常是通过改变进程组和会话ID来实现的,但在Java中,这个操作较复杂,...
- Java中的管道流是一种特殊类型的流,它允许数据从一个线程传输到另一个线程。它由`PipedInputStream`和`PipedOutputStream`组成。其中`PipedOutputStream`用于写入数据,而`PipedInputStream`则用于读取这些数据...
要获取子进程的输出,我们需要通过`Process`对象的`getInputStream`方法获取输出流,并将其重定向到父进程的控制台: ```java // Test_Exec_Out.java import java.io.*; public class Test_Exec_Out { public ...
在Java中,处理IO流涉及多个类和接口,主要集中在`java.io.*`包和新实现的`java.nio.*`包。 一、IO流的基本概念 Java的IO流分为输入流(InputStream)和输出流(OutputStream),它们分别用于读取和写入数据。数据...
2. **设置新的输出流**:通过调用 `System.setErr()` 和 `System.setOut()` 方法,可以将标准错误和标准输出重定向到指定的输出流。 示例代码如下: ```java // 创建一个新的输出流对象 java.io.OutputStream out ...
《Log4j将System.out重定向到Log4j输出详解》 在日志管理中,Log4j是一款广泛使用的开源日志框架,它允许开发者灵活地控制日志信息的输出方式和级别。当我们习惯于使用`System.out.println()`进行调试时,如何将...
实例013 重定向输出流实现程序日志 实例014 自动类型转换与强制类型转换 实例015 加密可以这样简单(位运算) 实例016 用三元运算符判断奇数和偶数 . 实例017 不用乘法运算符实现2×16 实例018 实现两个变量的...
4. 系统管理:例如,通过管道和重定向,可以组合多个命令,实现复杂的数据处理和系统监控任务。 四、实例分析 假设我们有一个名为`exp`的压缩包文件,其中可能包含一个简单的控制台通信程序,例如一个读取用户输入...
exec &> >(tee -a "$LOG_FILE") # 将标准输出和错误输出重定向到日志文件和控制台 java $JAVA_OPTS -jar $JAR_FILE_PATH ``` 这样的脚本将同时在控制台和指定的日志文件中记录应用程序的输出。 总结,使用`sh`脚本...
1. **日志重定向**:Tomcat默认配置下,标准输出(`System.out`)和标准错误(`System.err`)会被重定向到服务器的控制台日志中。如果Tomcat日志配置发生了变化或者日志文件达到了某个大小限制,那么新的输出可能会...
以下是一些关于Java的核心知识,包括接口与抽象类的区别、访问修饰符、流重定向、同步控制、序列化、垃圾回收以及线程管理等方面的问题及答案。 **Q1. 如何让Java类将程序消息发送到系统控制台,而将错误消息发送到...