如何在Java中指定和处理异常

1539847635(1).jpg

错误总是在软件世界中发生。它可能是无效的用户输入或无响应的外部系统,或者是一个简单的编程错误。在所有这些情况下,错误发生在运行时,应用程序需要处理它们。否则,它会崩溃并无法处理进一步的请求。Java提供了一种强大的机制,允许您处理发生的异常事件或调用堆栈中的一个较高方法。

在本文中,我们将介绍以下主题:

  • Java异常处理的通用术语
  • Java中的已检查和未经检查的异常
  • 如何处理异常
  • 如何指定例外
  • 如何知道是否处理或指定例外

在我们深入了解Java异常处理的细节之前,我们需要定义一些术语。

Java异常处理:常用术语

调用堆栈

调用堆栈是已调用以获取特定方法的有序方法列表。在这篇文章的上下文中,这些是被调用以获取发生错误的方法的方法。

我们来看一个例子吧。Method1调用m ethod2,它调用m ethod3。调用堆栈现在包含以下三个条目:

异常类和层次结构

异常类标识发生的错误类型。一个NumberFormatException异常,例如,被当抛出字符串有错误的格式,不能被转换成一个数字。

与每个Java类一样,异常类是继承层次结构的一部分。它必须扩展java.lang.Exception或其子类之一。

层次结构还用于对类似的错误进行分组。一个例子是IllegalArgumentException。它表示提供的方法参数无效,它是NumberFormatException的超类。

您还可以通过扩展Exception类或其任何子类来实现自己的异常类。以下代码段显示了一个自定义异常的简单示例。

 

异常对象

异常对象是异常类的实例。当发生异常事件而中断应用程序的正常流程时,它会被创建并传递给Java运行时。这称为“抛出异常”,因为在Java中,您使用关键字“throw”将异常传递给运行时。

当方法抛出异常对象时,运行时会在调用堆栈中搜索处理它的代码片段。我将在本文的“ 如何处理异常”部分中详细介绍异常处理。


Java中的已检查和未经检查的异常

Java支持已检查和未检查的异常。您可以以类似的方式使用它们,并且有很多关于何时使用哪种异常的讨论。但这超出了本文的范围。现在,让我们按照Oracle Java教程中介绍的方法进行操作。

对于您可以预期的所有异常事件以及编写良好的应用程序应该能够处理的事件,您应该使用已检查的异常。已检查的异常会扩展Exception 类。抛出已检查异常或调用指定已检查异常的方法的方法需要指定或处理它。

未经检查的异常会扩展RuntimeException。您应该将它们用于您无法预料的内部错误,并且通常情况下,应用程序无法从中恢复。方法可以但不需要处理或指定未经检查的异常。抛出未经检查的异常的典型示例是:

  • 缺少初始化的变量导致NullPointerException
  • 不正当使用导致IllegalArgumentException的API
如何处理异常

Java提供了两种不同的选项来处理异常。您可以使用try-catch-finally方法来处理各种异常。或者您可以使用try-with-resource方法,这样可以更轻松地清理资源。

try-catch-最后

这是在Java中处理异常的经典方法。它可以包括3个步骤:

  • 一个try  块,它包含可能抛出异常的代码段,
  • 一个或多个处理异常的catch
  • try块成功执行或处理抛出异常后执行的finally块。

块是必需的,你可以有或没有使用它捕获最终块。

试块

我们先来谈谈try块。它包含可能引发异常的代码部分。如果您的代码抛出多个异常,您可以选择是否要:

  • 对每个可能抛出异常的语句使用单独的try
  • 对可能引发多个异常的多个语句使用一个try块。

以下示例显示了一个包含三个方法调用的try块。

 

正如您在方法定义中所看到的,只有第一个和第三个方法指定了一个异常。第一个可能抛出MyBusinessException,而doEvenMore方法可能抛出NumberFormatException

在下一步中,您可以为要处理的每个异常类定义一个catch块,最后一个块。需要指定所有未由任何catch块处理的已检查异常。

Catch Block

您可以在catch块中实现一个或多个异常类型的处理。正如您在下面的代码片段中所看到的,catch子句将异常作为参数获取。您可以通过参数名称在catch块中引用它。

 

前面的代码示例显示了两个catch块。一个用于处理MyBusinessException,另一个用于处理NumberFormatException。两个块都以相同的方式处理异常。从Java 7开始,只需一个catch块即可完成相同的操作。

 

前面示例中catch块的实现非常基础。我只是调用printStackTrace方法,该方法将异常的类,消息和调用堆栈写入系统输出。

 

在实际应用程序中,您可能希望使用更高级的实现。例如,您可以向用户显示错误消息并请求其他输入,或者您可以将记录写入批处理的工作日志中。有时,甚至可以捕获并忽略异常。

在生产中,您还需要监控您的应用程序及其异常处理。这就是Retrace及其错误监控功能变得非常有用的地方。



最后一块

最后块获取的成功执行后,执行块或一后追赶块处理的异常。因此,它是实现任何清理逻辑的好地方,例如关闭连接或InputStream

您可以在以下代码段中看到此类清理操作的示例。的最后块将被执行,即使的实例化的FileInputStream抛出一个FileNotFoundException异常或文件内容的处理抛出的任何其它异常。

 

如您所见,finally块提供了防止任何泄漏的良好选择。在Java 7之前,最好将所有清理代码放入finally块中。

尝试与 – 资源

当Java 7引入了try-with-resource语句时,情况发生了变化。它会自动关闭实现AutoCloseable接口的所有资源。对于您需要关闭的大多数Java对象而言都是如此。

要使用此功能,唯一需要做的是在try子句中实例化对象。您还需要处理或指定关闭资源时可能抛出的所有异常。

以下代码片段使用try-with-resource语句而不是try-catch-finally语句显示上一个示例。

 

如您所见,try-with-resource语句更容易实现和读取。并且在关闭FileInputStream时可能抛出的IOException的处理不需要嵌套的try-catch语句。它现在由try-with-resource语句的catch块处理。

如何指定例外

如果您不在方法中处理异常,它将在调用堆栈中传播。如果它是一个已检查的异常,您还需要指定该方法可能抛出异常。您可以通过向方法声明添加throws子句来实现。因此,所有调用方法都需要自己处理或指定异常。

如果要指示方法可能会抛出未经检查的异常,您也可以指定此方法。

 

处理或指定例外

通常情况下,如果您应该处理或指定异常,则取决于用例。正如您可能猜到的那样,很难提供适合所有用例的建议。

一般来说,您需要问自己以下问题:

  1. 您是否能够在当前方法中处理异常?
  2. 你能预见到班上所有用户的需求吗?处理异常会满足这些需求吗?

如果您使用yes回答这两个问题,则应在当前方法中处理异常。在所有其他情况下,最好指定它。这允许类的调用者实现处理,因为它适合当前的用例。


摘要

好的,这就是现在的Java异常处理。我将在本系列的后续文章中详细介绍最佳实践和常见错误。

正如您所见,Java为您提供了两种常规类型的异常:已检查和未经检查的异常。

您应该对应用程序可以预期和处理的所有异常事件使用已检查的异常。您需要决定是否要在方法中处理它或者指定它。您可以使用try-catch-finally或try-with-resource块来处理它。如果您决定指定异常,它将成为方法定义的一部分,并且异常需要由所有调用方法指定或处理。

对于无法预料的内部错误,您应该使用未经检查的异常。您不需要处理或指定此类异常,但您可以采用与处理或指定已检查异常相同的方式执行此操作。