Java 序列化:在 try 或 finally 块中关闭流?

Java Serialization: close streams in try or in a finally block?

我正在查看 Java 序列化文章,并多次偶然发现在 try 块而不是 finally 块中关闭流的示例。有人可以向我解释这是为什么吗?

示例:

import java.io.*;
public class DeserializeDemo {

    public static void main(String [] args) {
      Employee e = null;
      try {
         FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      } catch(IOException i) {
         i.printStackTrace();
         return;
      } catch(ClassNotFoundException c) {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }

      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Number: " + e.number);
   }
}

来源:http://www.tutorialspoint.com/java/java_serialization.htm

最后你应该关闭连接。无论是在 try 还是 catch 中,finally 总是会执行。

此外,我们还需要在创建后关闭每个连接。

try{
  // statements 
}catch (){
 // statements 
}
finally {
    in.close();
    fileIn.close();
}

来自文档:

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs.

The runtime system always executes the statements within the finally block regardless of what happens within the try block. So it's the perfect place to perform cleanup.

所以这意味着如果您打开了一些连接、流或其他资源,您必须确保它们在您的代码块执行后关闭。

要避免这种丑陋的块,您可以使用实用方法:

public void close(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            // handle block
        }
    }
}

从 Java 8 开始(但不是必需的)您可以提供自己的异常处理程序和关闭资源:

public void close(Closeable closeable, Consumer<? extends Throwable> handler) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            handler.accept(ex);
        }
    }
}

另外,仅供参考,有 two 不调用 finally 块的情况。意思是在大多数情况下都会被调用。

您应该 在 finally 块中关闭

在 try 块中关闭是个坏习惯。

  try { 
     e = (Employee) in.readObject();   //Possibility of exception 
  } catch(IOException i) { 

  } catch(ClassNotFoundException c) { 

  } finally {
     in.close();
     fileIn.close();
  }

当有人知道代码会抛出异常而编写代码时,he/she 必须 close the opened resources

try-with-resources 语句

try-with-resources语句是声明一个或多个资源的try语句。资源是一个对象,在程序使用完它后必须关闭。 try-with-resources 语句确保在语句结束时关闭每个资源。任何实现 java.lang.AutoCloseable 的对象,包括所有实现 java.io.Closeable 的对象,都可以用作资源。

以下示例从文件中读取第一行。它使用 BufferedReader 的实例从文件中读取数据。 BufferedReader 是一个必须在程序完成后关闭的资源:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

在此示例中,try-with-resources 语句中声明的资源是BufferedReader。声明语句出现在紧跟在 try 关键字之后的括号内。 class BufferedReader,在 Java SE 7 及更高版本中,实现了接口 java.lang.AutoCloseable。因为 BufferedReader 实例是在 try-with-resource 语句中声明的,所以无论 try 语句是正常完成还是突然完成(作为方法 BufferedReader.readLine 抛出 IOException 的结果),它都会被关闭。

在 Java SE 7 之前,您可以使用 finally 块来确保关闭资源,无论 try 语句是正常完成还是突然完成。以下示例使用 finally 块而不是 try-with-resources 语句:

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

来源 =>http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

I was looking at Java Serialization articles and stumbled a number of times across examples where the streams are closed in the try block instead of in a finally block.

这样做的例子很差。虽然在 try 块中关闭流将适用于简单的一次性示例,但在代码可能被多次执行的情况下这样做可能会导致资源泄漏。

这个问题的其他答案很好地解释了关闭流的正确方法。

Can someone explain to me why is that?

归结为教程网站的质量控制不佳;即代码审查不充分。

您应该始终 closefinally 块中。 但是,您可以使用 try with resources.

这是 link:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

static String readFirstLineFromFile(String path) throws IOException {
 try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

接受的答案肯定有错误。

The close method can throw an IOException too. If this happens when in.close is called, the exception prevents fileIn.close from getting called, and the fileIn stream remains open.

当涉及多个流时,可以如下实现:

} finally {
 if ( in != null) {
  try { in .close();
  } catch (IOException ex) {
   // There is nothing we can do if close fails
  }
 }
 if (fileIn != null) {
  try {
   fileIn.close();
  } catch (IOException ex) {
   // Again, there is nothing we can do if close fails
  }
 }
}

或者,利用可关闭界面

} finally {
  closeResource(in);
  closeResource(fileIn);
}

方法:

private static void closeResource(Closeable c) {
 if (c != null) {
  try {
   c.close();
  } catch (IOException ex) {
   // There is nothing we can do if close fails
  }
 }
}

如果您 Java 7 或以上...

不要在 finally 块中关闭

close 方法可以抛出一个 IOException 并且 FileInputStream/ObjectInputStream 可以为 null。当你在finally中使用.close时,你必须再次检查null和try/catch。

改用"try-with-resources Statement"

使用 try-with-resources 您的代码如下所示:

try(
    FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
    ObjectInputStream in = new ObjectInputStream(fileIn)
) {
    e = (Employee) in.readObject();
    // in.close();
    // fileIn.close();
} 

try-with-resources语法保证实现AutoCloseable接口的资源会自动关闭。因此,您无需在代码中调用 close 方法。