作为编程新手,异常处理的概念可能很难使您耳目一新。并不是说这个概念本身很困难,而是该术语可以使它看起来比实际的更高级。
在本文中,您将了解异常是什么,异常为什么重要,如何使用它们以及避免常见错误。在大多数现代语言中,您可以随身携带这些技巧中的大多数。
了解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#仅具有未检查的异常)。已检查的异常必须被抛出异常的方法捕获,否则代码将无法编译。
要创建已检查的异常,请从
任何引发检查的异常的方法都必须使用 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异常用法指南
异常非常有用,但很容易被滥用和滥用。以下是一些技巧和最佳实践,可帮助您避免造成混乱。
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()
方法执行此操作。
提防过度使用异常。当您有锤子时,一切看起来都像钉子一样。当您初次了解异常时,您可能会觉得有义务将所有内容都转换为异常……以至于大多数应用程序的控制流程都归结为异常处理。请记住,异常是指“异常"事件!
现在您应该对异常足够熟悉,以了解异常的含义,使用原因以及如何将其合并到自己的代码中。如果您不完全了解这个概念,那就可以了!我花了好一会儿才能“点击"我的脑袋,所以就不必急着要它了。慢慢来。
标签: