如何正确处理Java异常

作为编程新手,异常处理的概念可能很难使您耳目一新。并不是说这个概念本身很困难,而是该术语可以使它看起来比实际的更高级。

在本文中,您将了解异常是什么,异常为什么重要,如何使用它们以及避免常见错误。在大多数现代语言中,您可以随身携带这些技巧中的大多数。

了解Java异常

在Java中,异常是表示异常的对象。 (或“例外")发生在您的应用程序运行期间。此类异常被抛出,这基本上意味着创建了一个异常对象(类似于“引发错误"的方式。)

美丽之处在于您可以捕获引发异常,它使您可以处理异常情况并允许您的应用程序继续运行,就好像什么都没出错。例如,尽管C中的空指针可能会使您的应用程序崩溃,但Java允许您在空变量有机会导致崩溃之前抛出并捕获 NullPointerException s。

记住,异常只是一个对象,但具有一个重要特征:必须从 Exception 类或 Exception 的任何子类扩展它。尽管Java具有各种内置的异常,但是您也可以根据需要创建自己的异常。一些最常见的Java异常包括:

  • NullPointerException
  • NumberFormatException
  • IllegalArgumentException
  • RuntimeException
  • IllegalStateException
  • 那么当您抛出异常时会发生什么?

    首先,Java在即时方法中查找是否有处理您抛出的异常的代码。如果处理程序不存在,它将查看调用当前方法的方法,以查看其中是否存在句柄。如果不是,它将查看调用 that 方法的方法,然后查看下一个方法,等等。如果未捕获到异常,则应用程序将打印堆栈跟踪,然后崩溃。 / strong>(实际上,它比崩溃简单得多,但这是本文讨论范围之外的高级主题。)

    堆栈跟踪是Java遍历的所有方法的列表。在寻找异常处理程序时。堆栈跟踪如下所示:

    Exception in thread "main" java.lang.NullPointerException  at com.example.myproject.Book.getTitle(Book.java:16)  at com.example.myproject.Author.getBookTitles(Author.java:25)  at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

    我们可以从中收集很多东西。首先,抛出的异常是 NullPointerException 。它发生在Book.java第16行的 getTitle()方法中。该方法是从Author.java的第25行的 getBookTitles()中调用的。从Bootstrap.java第14行的 main()中调用了那个方法。如您所见,了解所有这些信息会使调试更加容易。

    但是,再次,异常的真正好处是,您可以通过捕获异常,正确设置并恢复异常来“处理"异常情况。应用程序而不会崩溃。

    在代码中使用Java异常

    假设您有 someMethod(),该整数需要一个整数并执行一些逻辑,如果整数小于0或大于100。这可能是引发异常的好地方:

    public void someMethod(int value) {  if (value < 0 || value > 100) {    throw new IllegalArgumentException();  }  // ...}

    要捕获此异常,您需要转到 someMethod()被调用并使用 try-catch块

    public void callingMethod() {  try {    someMethod(200);    someOtherMethod();  } catch (IllegalArgumentException e) {    // handle the exception in here  }  // ...}

    try 块中的所有内容都会按顺序执行,直到引发异常为止。引发异常后,所有后续语句都会被跳过,应用程序逻辑将立即跳转到 catch 块。

    在我们的示例中,我们输入try块并立即调用 someMethod()。由于200不在0到100之间,因此会抛出 IllegalArgumentException 。这样会立即结束 someMethod()的执行,跳过try块中的其余逻辑(永远不会调用 someOtherMethod()),并在catch块内恢复执行。

    如果我们改为调用 someMethod(50)会发生什么?永远不会抛出 IllegalArgumentException someMethod()将照常执行。 try块将正常执行,在someMethod()完成时调用 someOtherMethod()。当 someOtherMethod()结束时,捕获块将​​被跳过,而 callingMethod()将继续。

    请注意,每次尝试可以有多个捕获块块:

    public void callingMethod() {  try {    someMethod(200);    someOtherMethod();  } catch (IllegalArgumentException e) {    // handle the exception in here  } catch (NullPointerException e) {    // handle the exception in here  }  // ...}

    还请注意,还存在一个可选的 finally 块:

    public void method() {  try {    // ...  } catch (Exception e) {    // ...  } finally {    // ...  }}

    finally块中的代码始终是 >不管执行什么。如果try块中有return语句,则在返回该方法之前执行finally块。如果在catch块中引发了另一个异常,则在引发异常之前将执行finally块。

    当方法结束之前需要清除对象时,应使用finally块。例如,如果您在try块中打开了一个文件,然后又引发了异常,则finally块使您可以在离开方法之前关闭文件。

    请注意,可以有一个没有catch块的finally块:

    public void method() {  try {    // ...  } finally {    // ...  }}

    这使您可以进行任何必要的清理,同时允许抛出的异常在方法调用堆栈中传播(即,您不想在这里处理异常,但您仍然需要先清理)。

    与大多数语言不同,Java区分已检查的异常未检查的异常(例如,C#仅具有未检查的异常)。已检查的异常必须被抛出异常的方法捕获,否则代码将无法编译。

    要创建已检查的异常,请从 Exception < / code>。要创建未检查的异常,请从 RuntimeException 扩展。

    任何引发检查的异常的方法都必须使用 throws 关键字在方法签名中对此进行说明。由于Java内置的 IOException 是已检查的异常,因此不会编译以下代码:

    public void wontCompile() {  // ...  if (someCondition) {    throw new IOException();  }  // ...}

    您必须首先声明它引发了已检查的异常:

    public void willCompile() throws IOException {  // ...  if (someCondition) {    throw new IOException();  }  // ...}

    请注意,可以将方法声明为引发异常,但从不实际引发异常。即使这样,仍将需要捕获异常,否则代码将无法编译。

    何时应使用选中或未选中的异常?

    官方Java文档中有一页关于这个问题。它用简洁的经验法则总结了这种差异:“如果可以合理地期望客户从异常中恢复,则将其作为已检查的异常。如果客户端无法采取任何措施来从异常中恢复,请将其设置为未经检查的异常。"

    但是该准则可能已过时。一方面,检查异常确实导致代码更健壮。另一方面,没有其他语言可以像Java一样检查异常,这表明了两件事:一,该功能不足以让其他语言窃取它;其二,您可以完全没有它们。另外,受检查的异常与Java 8中引入的lambda表达式不能很好地配合。

    Java异常用法指南

    异常非常有用,但很容易被滥用和滥用。以下是一些技巧和最佳实践,可帮助您避免造成混乱。

  • 将特定异常替换为一般异常。在< code> IllegalArgumentException ,否则,在 RuntimeException 上使用 IllegalArgumentException
  • 永远不要捕获 Throwable ! Exception 类实际上扩展了 Throwable ,并且catch块实际上与 Throwable 或任何扩展Throwable的类一起工作。但是, Error 类也扩展了 Throwable ,并且您永远不想捕获 Error ,因为 Error 表示严重
  • 永远不要捕获 Exception InterruptedException 扩展了 Exception ,因此任何阻止catchs Exception 也将捕获 InterruptedException ,这是一个非常重要的异常,除非您知道自己不了解,否则您不希望将其弄乱(尤其是在多线程应用程序中)在做。如果您不知道要捕获哪个异常,请考虑不捕获任何内容。
  • 使用描述性消息来简化调试。引发异常时,可以提供 String 消息作为参数。可以使用 Exception.getMessage()方法在catch块中访问此消息,但是如果从未捕获到异常,则该消息也将显示为堆栈跟踪的一部分。
  • 尽量不要捕获并忽略异常。为了避免检查异常带来的不便,许多新手和懒惰的程序员都会设置catch块,但将其留空。坏!请始终优雅地处理它,但是,如果不能,请至少打印出堆栈跟踪,以便您知道已引发异常。您可以使用 Exception.printStackTrace()方法执行此操作。
  • 提防过度使用异常。当您有锤子时,一切看起来都像钉子一样。当您初次了解异常时,您可能会觉得有义务将所有内容都转换为异常……以至于大多数应用程序的控制流程都归结为异常处理。请记住,异常是指“异常"事件!
  • 现在您应该对异常足够熟悉,以了解异常的含义,使用原因以及如何将其合并到自己的代码中。如果您不完全了解这个概念,那就可以了!我花了好一会儿才能“点击"我的脑袋,所以就不必急着要它了。慢慢来。

    标签: