Сначала начните с try-with-resources, 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");
}
и вы бы точно не вызвали c1.close()
в блоке try, поскольку с1 вообще не было бы.
Имея это в виду, выбрасывать исключение из c2, потому что содержащийся c1 не закрыт, абсолютно неверен, фактически 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
, он закрывается и обнуляется (в блоке finally, так происходит даже в случае исключения), и все в поточно-безопасном блоке. (cb
- это просто массив символов, он также обнуляется, что немного упрощает работу сборщика мусора). Из-за обнуления всего в блоке finally любые дополнительные вызовы этого же метода ничего не сделают (кроме синхронизации на мгновение с блокировкой).