有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java继承和TryWithResources

假设有两个类实现AutoCloseable接口,如下所示:

public class Closing1 implements AutoCloseable {

private boolean closed;

@Override
public void close() throws Exception {
    if (closed) {
        throw new Exception("Closed Already");
    }
    this.closed = true;
    System.out.println("Closing1 closed");
}

public boolean isClosed() {
    return closed;
}

}

public class Closing2 implements AutoCloseable {

private Closing1 cl1;

public Closing2(Closing1 cl1) {
    this.cl1 = cl1;
}

@Override
public void close() throws Exception {
    if(!cl1.isClosed()) {
        throw new Exception("Closing1 not closed");
    }
    System.out.println("Closing2 closed");
}

}

我发现try-with-resources的所有变体都会导致异常!这里有没有我遗漏的东西,或者这只是TWR的设计方式

        try(Closing1 c1 = new Closing1();Closing2 c2 = new Closing2(c1)){
            System.out.println("Done");
        } //Exception while auto closing C2

        try(Closing1 c1 = new Closing1();Closing2 c2 = new Closing2(c1)){
            System.out.println("Done");
            c1.close();
        } // exception while auto closing c1

共 (2) 个答案

  1. # 1 楼答案

    首先尝试使用资源,https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
    正如第一个示例所示:

    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
    

    人们不一定会说出链中的每件事

    除非您明确地需要c1来完成某些事情(而不是结束),否则在现实生活中,您的代码片段更像

    try(Closing2 c2 = new Closing2(new Closing1())){
        System.out.println("Done");
    }
    

    而且您肯定不会在try块中调用c1.close(),因为根本没有c1

    记住这一点,因为包含的c1未关闭而从c2引发异常是完全错误的,实际上c2拥有Closing1对象,并且应该对其调用close()

    class Close1 implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("Closing c1");
        }
    }
    
    class Close2 implements AutoCloseable {
        Close1 c1;
        Close2(Close1 c1) {
            this.c1=c1;
        }
    
        @Override
        public void close() throws Exception {
            System.out.print("Closing c1 from c2: ");
            c1.close();
            System.out.println("Closing c2");
        }
    }
    
    void test() {
        System.out.println("Before try block");
        try(Close2 c2=new Close2(new Close1())) {
            System.out.println("In try block");
        }
        catch(Exception ex) {
            System.out.println("Exception: "+ex);
        }
        finally {
            System.out.println("In finally block");
        }
        System.out.println("After try block");
    }
    

    但是,如果有人给c1命名,它将被关闭两次,这就是幂等性出现的地方,正如已经有人建议的:

    System.out.println("Before try block");
    try(Close1 c1 = new Close1(); Close2 c2 = new Close2(c1)){
        System.out.println("In try block");
    }
    catch(Exception ex){
        System.out.println("Exception: "+ex);
    }
    finally{
        System.out.println("In finally block");
    }
    System.out.println("After try block");
    

    正如前面提到的BufferedReader,这是它的close()方法:

    public void close() throws IOException {
        synchronized (lock) {
            if (in == null)
                return;
            try {
                in.close();
            } finally {
                in = null;
                cb = null;
            }
        }
    }
    

    如果它有in,它将被关闭并为null(在finally块中,即使发生异常也会发生),所有这些都在线程安全块中。(cb只是一个字符数组,它也会变为null,从而稍微简化了垃圾收集器的使用寿命)。由于将finally块中的所有内容置零,因此对同一方法的任何额外调用都不会做任何事情(除了在锁上同步一段时间之外)

  2. # 2 楼答案

    Try with resources将按与其声明相反的顺序关闭资源。这意味着将首先调用c2.close(),这将在编码异常时引发异常