Java 相当于 python 的 "with"

Java equivalent to python's "with"

Python 有一个很好的特性:"with" 语句。它对于进行影响语句中调用的所有代码的全局更改很有用。

例如您可以定义一个 class CapturePrintStatements 来捕获在 "with"

中调用的所有打印语句
with CapturePrintStatements() as c:
    print 'Stuff Done'
print 'More Stuff Done'
assert c.get_text() == 'Stuff Done'

在Java中有对应的吗?

try-with-resources 是它的 Java 等价物,在 Java 7 及更高版本中可用。

这使您可以处理需要明确关闭的资源,而不必担心关闭它们。例如:

之前 Java7:

InputStream input = null;

try {
    input = new FileInputStream("myFile.txt");

} finally {
    if(input != null){
        input.close();
    }
}

Java 7 岁及以上:

try(FileInputStream input = new FileInputStream("myFile.txt")) {
    // Do something with the InputStream
}

这是 try-with-resources 结构。当执行流离开 try 块时,FileInputStream 自动关闭 。这是因为 FileInputStream 实现了 AutoCloseable 接口。

正如 Mohammed 所说,您可以使用 try-with-resources。在这种情况下,你想拥有自己的资源,其实并不难。

正在创建可自动关闭的 class

首先,您的 class 应该实施 AutoCloseable:

public class CaptureOutput implements AutoCloseable {

构建这个class时,你应该

  • 存储旧的 System.out
  • 创建一个PrintStream来替换它(参见Java: PrintStream to String?)和
  • 将默认流替换为 System.setOut()

这是我们的做法

    public CaptureOutput() {
        this.stream = new ByteArrayOutputStream();
        this.out = System.out;

        System.setOut(new PrintStream(stream));
    }

秘诀在于 AutoCloseable.close() 方法:您只需在此处撤消替换即可:

    public void close() throws Exception {
        System.setOut(this.out);
    }

最后,您需要一个检索内容的方法:

    public String getContent() {
        return this.stream.toString();
    }

使用 try-with-resources

完成后,只需将 CaptureOutput 传递给 try 子句。例如下面的代码...

public static void main(String[] args) throws Exception {
    String content = null;

    System.out.println("This will be printed");

    try (CaptureOutput co = new CaptureOutput()) {
        System.out.println("EXAMPLE");

        content = co.getContent();
    }

    System.out.println("This will be printed, too.");

    System.out.println("The content of the string is " + content);
}

...将产生以下结果:

This will be printed
This will be printed, too.
The content of the string is EXAMPLE

范围问题

请注意,我们不会在最后一行调用 co.getContent()。这是不可能的,因为与 Python 不同,co 变量的范围在 try 子句内。 try 块完成后,它就消失了。[1]这就是我们从块内部获取值的原因。

没那么优雅吧?一个解决方案可能是将 BAOS 交给 CaptureOutput 构造函数:

    public CaptureOutput(ByteArrayOutputStream stream) {
        this.stream = stream;
        this.out = System.out;

        System.setOut(new PrintStream(this.stream));
    }

现在,我们稍后使用流:

    public static void main(String[] args) throws Exception {
        System.out.println("This will be printed");

        ByteArrayOutputStream stream = new ByteArrayOutputStream();

        try (CaptureOutput co = new CaptureOutput(stream)) {
            System.out.println("EXAMPLE");
        }

        System.out.println("This will be printed, too.");

        System.out.println("The content of the string is " + stream.toString());
    }

(此外,不可能在 try 之前创建 CaptureOutput 变量。这是有道理的:AutoCloseable 对象应该在它们之后 "closed"使用。毕竟,关闭文件有什么用?我们的用例与那个有点不同,所以我们必须依赖替代方案。)

完整的 classes

这里是完整的 classes:

  • CaptureOutput.java:

    import java.io.ByteArrayOutputStream;
    import java.io.PrintStream;
    
    public class CaptureOutput implements AutoCloseable {
    
        private ByteArrayOutputStream stream;
        private PrintStream out;
    
        public CaptureOutput(ByteArrayOutputStream stream) {
            this.stream = stream;
            this.out = System.out;
    
            System.setOut(new PrintStream(this.stream));
        }
    
        public CaptureOutput() {
            this(new ByteArrayOutputStream());
        }
    
        @Override
        public void close() throws Exception {
            System.setOut(this.out);
        }
    
        public String getContent() {
            return this.stream.toString();
        }
    
    }
    
  • Main.java:

    import java.io.ByteArrayOutputStream;
    
    public class Main {
    
        public static void main(String[] args) throws Exception {
            System.out.println("This will be printed");
    
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
    
            try (CaptureOutput co = new CaptureOutput(stream)) {
                System.out.println("EXAMPLE");
            }
    
            System.out.println("This will be printed, too.");
    
            System.out.println("The content of the string is " + stream.toString());
        }
    
    }