JavaRush /Java 博客 /Random-ZH /茶歇#203。如何使用 try-with-resource 语句处理异常

茶歇#203。如何使用 try-with-resource 语句处理异常

已在 Random-ZH 群组中发布
来源:Medium 本指南介绍了 try-with-resource 相对于 try-catch-finally 的优势。您还将了解在什么条件下引发抑制异常以及如何将 try-with-resources 与多个资源一起使用。 try with resources茶歇#203。 如何使用 try-with-resource 语句处理异常 - 1构造,也称为try-with-resources,是 Java 中的一种异常处理机制,可以在完成使用 Java InputStream 或 JDBC Connection 等资源时自动关闭这些资源。2011年,Oracle在Java语言语法中添加了try with resources,以确保网络套接字、数据库连接、文件和文件夹链接等对象在使用后能够正常关闭。如果开发人员打开这些资源的句柄后未能关闭这些资源,可能会导致内存泄漏、触发可预防的垃圾收集例程以及服务器上的 CPU 开销。

Java 7 之前

在Java中,如果使用输入/输出流等资源,则总是需要在使用后关闭它们。由于它们也可以抛出异常,因此它们必须包含在try-catch块中。结束必须发生在finally块中。至少在 Java 7 之前是这样。但它有几个缺点:
  • 在关闭资源之前,您需要检查资源是否为
  • 闭包本身可能会引发异常,因此您必须在finally块中再添加一个try-catch
  • 程序员往往会忘记关闭他们的资源。

如何使用try-with-resource语句?

该运算符最初是在 Java 7 中引入的,其想法是开发人员不再需要担心管理他们在try-catch-finally块中使用的资源。这是通过消除对finally块的需要来实现的,实际上,开发人员仅使用finally块来关闭资源。在 Java 中,try-with-resources语句是声明一个或多个资源的try语句。资源是程序终止后必须关闭的对象。当代码执行离开try-with-resources块时,在try-with-resources块中打开的任何资源都会自动关闭,无论在try-with-resources 块内或尝试关闭资源时是否引发任何异常。要使用 Java try-with-resources语言功能,请遵循以下规则:
  • try-with-resources语句控制的所有对象都必须实现AutoCloseable接口。
  • 可以在try-with-resources块中创建多个AutoCloseable对象。
  • 在try-with-resources语句中声明的对象在try块中执行,但不在catchfinally块中执行。
  • 无论运行时是否抛出异常,都会调用在try-with-resources块中声明的对象的close()方法。
  • 如果在close()方法中抛出异常,则它可能被归类为抑制异常。
Catchfinally块仍然可以在try-with-resource块中使用,并且它们的工作方式与在常规try块中相同。如果使用try-with-resource ,则AutoCloseable对象引用的资源将始终被关闭。这消除了通常由资源分配不当引起的潜在内存泄漏。

句法

try(declare resources here) {
    // использовать ресурсы
}
catch(FileNotFoundException e) {
    // обработка исключений
}

try-with-resource 的实际使用

要自动关闭,必须在try内声明并初始化资源:
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
    writer.println("Hello World");
}

用 try-with-resources 替换 try-catch-finally

使用try-with-resources功能的一个简单而明显的方法是替换传统且冗长的try-catch-finally块。我们来比较一下下面的代码示例。第一个示例是典型的try-catch-finally块:
Scanner scanner = null;
try {
    scanner = new Scanner(new File("test.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}
这是一个使用try-with-resources 的新的超级简洁解决方案:
try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

try 和 try-with-resource 之间的区别

当涉及到异常时,try-catch-finally块和try-with-resources块之间存在差异。tryfinally块中都会引发异常,但该方法仅返回finally块中引发的异常。对于try-with-resources,如果try块和try-with-resources语句中发生异常,该方法将返回try块中抛出的异常。try-with-resources块抛出的异常是被抑制的,也就是说,try-with-resources块抛出的异常是被抑制的。

为什么应该使用 try-with-resource 语句?

try-with-resources语句确保每个资源在语句结束时关闭。如果我们不关闭资源,这可能会导致资源泄漏,并且程序可能会耗尽可用的资源。当您使用try-catch-finally块时会发生这种情况。在Java SE 7之前,您可以使用finally块来确保无论try语句是正常退出还是突然退出,资源都会被关闭。以下示例使用finally块而不是try-with-resources语句:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {

    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    try {
        return br.readLine();
    } finally {
        br.close();
        fr.close();
    }
}
此示例中可能存在资源泄漏。程序必须做的不仅仅是依靠垃圾收集器在使用完资源后释放资源的内存。程序还必须将资源返回给操作系统,通常是通过调用资源的 close 方法。但是,如果程序在垃圾收集器返回资源之前不执行此操作,则释放资源所需的信息将丢失。操作系统仍认为正在使用的资源已泄漏。在上面的示例中,如果readLine方法抛出异常,并且finally块中的br.close()语句抛出异常,则FileReader已泄漏。这就是为什么您最好使用try-with-resources语句而不是finally块来关闭程序的资源。

再举一个例子

以下示例从文件中读取第一行。FileReaderBufferedReader实例用于读取数据。这些是程序退出后需要关闭的资源。
import java.io.*;
class Main {
  public static void main(String[] args) {
    String line;
    try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    }
  }
}
如您所见,try-with-resources语句中声明的资源是BufferedReader。该资源的声明语句出现在紧跟在try关键字后面的括号中。Java SE 7 及更高版本中的BufferedReader类实现java.lang.AutoCloseable接口。由于BufferedReader实例是在try-with-resource语句中声明的,因此无论try语句正常退出还是突然退出(如果BufferedReader.readLine()方法抛出IOException),它们都会被关闭。

抑制异常

如果try块抛出异常,并且try-with-resources块中出现一个或多个异常,则try-with-resources块抛出的异常将被抑制。换句话说,我们可以说try-with-resources抛出的异常是抑制异常。您可以使用Throwable类的getSuppress()方法捕获这些异常。在前面显示的示例中,在以下条件下,try-with-resources语句引发异常:
  • 找不到文件test.txt 。
  • 关闭BufferedReader对象。
try块也可能引发异常,因为读取文件随时可能因多种原因而失败。如果try块和try-with-resources语句都抛出异常,则在第一种情况下会抛出异常,在第二种情况下会抑制异常。

获取被抑制的异常

在 Java 7 及更高版本中,可以通过从try块引发的异常调用Throwable.getSuppressed()方法来获取受抑制的异常。 getSuppress()返回一个数组,其中包含由try-with-resources语句抑制的所有异常。如果没有抑制异常或禁用抑制,则返回空数组。以下是在catch块中接收抑制异常的示例:
catch(IOException e) {
  System.out.println("Thrown exception=>" + e.getMessage());
  Throwable[] suppressedExceptions = e.getSuppressed();
  for (int i=0; i" + suppressedExceptions[i]);
  }
}

使用资源尝试的好处

  • 可读且易于编写代码。
  • 自动资源管理。
  • 代码行数已减少。
  • 当在try-with-resources中打开多个资源时,它们会以相反的顺序关闭,以避免依赖问题。
当然,关闭资源不再需要finally块。以前,在Java 7之前,我们必须使用finally块来确保资源被关闭以避免资源泄漏。这是一个与第一个示例类似的程序。在这个程序中,我们使用了finally块来关闭资源。
import java.io.*;
class Main {
  public static void main(String[] args) {
    BufferedReader br = null;
    String line;
    try {
      System.out.println("Entering try block");
      br = new BufferedReader(new FileReader("test.txt"));
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    } finally {
      System.out.println("Entering finally block");
      try {
        if (br != null) {
          br.close();
        }
      } catch (IOException e) {
        System.out.println("IOException in finally block =>"+e.getMessage());
      }
    }
  }
}
结论:
输入 try 块 Line => test.txt 文件中的行 输入 finally 块
从上面的示例中可以看出,使用finally块来清理资源会增加代码的复杂性。注意到finally块中的try...catch 块了吗?这是因为在finally块内关闭BufferedReader实例时也可能发生IOException ,因此它也会被捕获并处理。try-with-resources语句执行自动资源管理。我们不需要显式关闭资源,因为 JVM 会自动关闭它们。这使得代码更具可读性并且更容易编写。

尝试使用多种资源

我们可以在try-with-resources块中声明多个资源,只需用分号分隔它们即可:
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    while (scanner.hasNext()) {
	writer.print(scanner.nextLine());
    }
}

Java 9 - 有效的最终变量

在 Java 9 之前,我们只能在try-with-resources块中使用新变量:
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    // omitted
}
请注意,在声明多个资源时,这非常冗长。从 Java 9(JEP 213 更新)开始,我们可以在try-with-resources块中使用Final甚至有效的 Final变量:
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
    // omitted
}
简而言之,如果一个变量在第一次赋值后没有被修改,那么它实际上就是final,即使它没有显式标记为final。如上所示,扫描仪变量被显式声明为最终变量,因此我们可以将其与try-with-resources块一起使用。尽管 writer 变量不是显式的final,但它在第一次赋值后不会改变。所以我们也可以使用writer变量。我希望今天您能够更好地理解如何使用try-with-resource语句处理异常。快乐学习!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION