使用try-with-resources时,某些自定义类实现Closeable接口时如何自动创建FileInputStream实例

问题描述

我一直在使用 try-with-resources 语句。

try(FileReader rd = new FileReader("Test.txt");){} 
catch (Exception e) {e.printstacktrace();}

对资源使用 try 的好处主要是避免指定 finally 块来关闭资源。

这就是我的研究过程开始的地方。

经过调试,我发现 FileReader 扩展了 InputStreamReader。里面 FileReader 类此构造函数调用

public FileReader(File file) throws FileNotFoundException {
  super(new FileInputStream(file));
}

创建 FileInputStream 类的对象。 FileInputStream 扩展了实现 Closeable 接口的 InputStream

在 FileInputStream 类内部调用 close 方法,如下所示,并使用本机方法执行关闭资源所需的操作。

public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
           channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

所以我明白这就是调用 close 方法的方式。

现在当我使用一些自定义类实现 Closeable 接口时我无法理解 直接如

public class MyClass implements Closeable 
    {
        public void close() 
       {
         System.out.println("connection closed...");
       }
    }

并像这样使用它

try(MyClass rd = new MyClass();)
{} 
catch (Exception e) 
{e.printstacktrace();}

它仍然自动调用自定义MyClass 中的 close 方法,而我没有明确调用它。 当我运行调试时,它将进入 FileInputStream 类,该类扩展了实现 Closeable 接口的 InputStream 类。然后最后这个方法调用

public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
           channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

有人可以向我解释一下 FileInputStream 实例/对象是如何创建的吗?

提前致谢。

解决方法

背景

在解决您的实际问题之前,了解try-with-resources 的工作原理很重要。为此,我们可以查看 §14.20.3 of the Java Language Specification (JLS)。基本上,该章节告诉您的是,如果您有以下代码:

try (AutoCloseable closeable = ...) {
  // try something
}

注意: try-with-resources 语句适用于任何 java.lang.AutoCloseable 实现。 java.io.Closeable 接口扩展了 java.lang.AutoCloseable 接口。

然后由编译器翻译,就像你写的一样:

AutoCloseable closeable = ...;
Throwable primary = null;
try {
  // try something
} catch (Throwable t) {
  primary = t;
  throw t;
} finally {
  if (closeable != null) {
    if (primary != null) {
      try {
        closeable.close();
      catch (Throwable suppressed) {
        primary.addSuppressed(suppressed);
      }
    } else {
      closeable.close();
    }
  }
}

以上是“基本”try-with-resources 的翻译,其中原始代码中没有 catchfinally 块。当您确实有 catch 和/或 finally 块(即“扩展的”try-with-resources)时:

try (AutoCloseable closeable = ...) {
  // try something
} catch (Exception ex) {
  // handle error
} finally {
  // finally...
}

然后它简单地将我们之前的内容包装在另一个 try-catch-finally 块中:

try {
  // "basic" translation
} catch (Exception ex) {
  // handle error
} finally {
  // finally...
}

如您所见,这里没有什么特别的事情发生。一切都相当简单和独立。


你的问题

回答您的问题:FileInputStream 不是作为try-with-resources 语句的一部分隐式创建的。鉴于您的 MyClass 实现,您在调试代码时声称看到的内容是不可能的。您必须调试不同的代码段,或者您的源代码与编译的代码不同步。我的猜测是前者。如果您要调试以下内容:

import java.io.Closeable;
import java.io.IOException;

public class Main {

  public static void main(String[] args) {
    try (MyClass foo = new MyClass()) {
      // try something...
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  public static class MyClass implements Closeable {
    
    @Override
    public void close() throws IOException {
      System.out.println("MyClass#close()");
    }
  }
}

您不会看到 FileInputStream#close() 被调用——无论如何也不会被该代码调用。为了看到这一点,我们可以根据 JLS 的 §14.20.3 翻译上述代码,其中给出:

import java.io.Closeable;
import java.io.IOException;

public class Main {

  public static void main(String[] args) {
    MyClass foo = new MyClass();
    Throwable primary = null;
    try {
      try {
        // try something...
      } catch (Throwable t) {
        primary = t;
        throw t;
      } finally {
        if (foo != null) {
          if (primary != null) {
            try {
              foo.close();
            } catch (Throwable suppressed) {
              primary.addSuppressed(suppressed);
            }
          } else {
            foo.close();
          }
        }
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  public static class MyClass implements Closeable {

    @Override
    public void close() throws IOException {
      System.out.println("MyClass#close()");
    }
  }
}