illegalargumentexception是什么异常_java异常处理分析

处理 Java 中的异常情况并不是个轻松的话题,什么样的异常需要如何处理,常让新手们觉得难以理解,甚至有经验的开发人员也可能花几个小时讨论不决。

正因如此,大多数开发团队有着一套自己的规则,如果你是团队里的新人,你会对团队之间对于这个规则的巨大差别感到惊讶。

即便是这样,也有一些大多数团队都使用的好做法。下面列出了 9 个比较重要的处理方式,可以帮你上手或者提升你的异常处理能力。

1、清理 Finally 块中的资源,或使用 Try-With-Resource 语句

在 try 块中使用资源时常发生,比如 InputStream ,是需要在用完之后关掉的。一个通常发生的错误是,在 try 块的末尾关掉资源:

d9a2f55b1bee532b280b0bd88fe1d30a.png

顺利的话,只要没有异常抛出,这种方法似乎可以工作得很好,try 中的所有语句都会被执行,然后资源也被关掉。

不过你因为某个原因添加了 try ,其中调用的一个或多个方法可能就会引发异常,甚至有可能是你自己引发了异常,此时就无法运行到 try 的结尾,结果资源没法被关掉。

因此,你应该把清理资源的代码都放在 finally 块中,或者使用 Try-With-Resource 语句。

使用 finally 块

和 try 中的最后几行不一样,无论是 try 块被成功执行后还是在 catch 块中处理了异常,finally 块总是能被执行。这样,你可以确保清掉所有打开的资源。

13c4b04c98d665a9f5c99eb72fba9b35.png

Java 7 的 Try-With-Resource 语句

另一个办法是使用 Try-With-Resource 语句。

如果你使用的资源实现接口是 AutoCloseable,就可以用这个语句,多数Java 标准资源都这么做。当你在 try 中打开资源,它可以在 try 执行完毕后或异常处理完后自动关闭资源。

2、更喜欢特定的异常

你引发的异常越具体越好。时刻想着那些并不知道你代码的同伴,或者几个月之后的你,他们需要调用你的方法处理异常。

所以,要尽可能地提供更多信息,确保你的 API 更容易理解。这样,调用你的方法的人才能更好地处理异常,或者避免在检查上浪费多余的时间。

要想办法找到那个最合适你期望事件的类,比如引发一个 NumberFormatException 比IllegalArguementException 要好,请避免引发一个不明确的异常。

bcfc0d1e38dbeb7a930390393e0feaf0.png

3、把指定的异常记录下来

无论在什么时候你在方法签名中指定了一个异常,你都应该在你的 Javadoc 中记录下来。这和上一个做法目的相同,都是给调用者尽量多的信息,便于他们避免或者处理异常。

所以,要确保你在 Javadoc 中添加了 @throws 申明,说明什么样的情况会引发异常。

4、用描述性消息引发异常

这条和前两条背后的想法差不多,只是这次不用给方法调用者提供什么信息了。因为日志文件中或者监视器里收到异常报告时,每个人都必须读取异常消息。

因此,应该尽可能准确地描述问题,并提供最相关的信息帮助了解异常事件。

请不要误解我,我并不是让你写一大段文字。你应该在一两句话中为该异常作出解释,这能帮助你的运维团队了解问题的严重程度,同时也让你在分析服务异常时更轻松。

如果你引发了一个特定的异常,它的类名就可能已经描述了错误类型,所以你也不用再提供更多信息了。NumberFormatException 是个比较好的例子,当你在给一个字符串提供了错的格式,类 java.lang.Long 的构造函数就会引发这个异常。

5、优先抓住最具体的异常

多数 IDE 可以帮助你实现这一条。当你在尝试优先捕获较少特定的异常时,它们会报告一个无法访问的代码块。

问题在于,只有与异常匹配的第一个 catch 块会被执行,如果你先抓住了 IllegalArgumentException,你就无法到达应该处理更具体的 NumberFormatException,因为NumberFormatException 是其子类。

总是优先捕获最具体的异常类,并将低精确度的 catch 块添加到列表末尾。

在以下代码段中,你可以看到一个这样的 try-catch 语句的示例。第一 catch 块处理所有的NumberFormatException,第二个则是处理所有非 NumberFormatException 的IllegalArgumentException。

65c8a80345793103b012cc7da696d2b5.png

6、别去抓可抛出的对象

可抛出(throwable)是所有异常和错误的超类,虽然你可以在 catch 语句中使用,但是你永远都不应该用它。

如果你在 catch 语句中使用可抛出对象,结果不仅仅是抓住所有的异常,它还会抓出所有的错误。错误由 JVM 抛出,会指出那些不打算被应用程序处理的严重问题。这一类型的代表例子是 OutOfMemoryError,以及 StackOverflowError,两者都是由不在应用控制之内的情况引发的,无法处理。

所以,除非你绝对确定处于异常情况下,并且你有能力或被要求去处理错误,否则不要去抓可抛出对象。

7、不要忽视异常

你有没有分析过一个只有用例的第一部分被执行了的错误报告?

这种情况的原因往往是一个被忽视的异常,那个开发者可能非常确定它不会再被抛出,还添加了一个并不能处理或者记录异常的 catch 块。然后,当你发现这个块时,你还很有可能找到一个名为“永远不会发生”的著名评论。

75ee63fc34596fa7e29dcab4e1366c2b.png

你当然有可能在分析一个会发生不可能的问题。

所以,不要忽视一个异常。你不知道代码将来会发生怎样的改变,可能有人会删除你用来防止异常事件的验证,全然不知这会导致问题。或者,抛出异常的代码被修改,导致在同一个类里抛出多个异常,而且调用代码也不能阻止所有的异常了。

8ee9fc82490eebc2d7f159003a93914c.png

你至少应该写一个日志消息,告诉大家发生了难以想象的事情,需要有人做检查。

8、别记录完又抛出

这一条可能是这个列表中最常被忽视的。不过,你总是能找到很多代码段甚至是库里,有异常被捕获、记录然后又抛出。

219fbaa0a8a91b1dc3632f5f6f05d228.png

一般大家的直觉是,异常发生时记录它然后再把它抛出,这样可以方便调用者妥善处理该异常。可问题是往往大家会为同一个异常写入多个错误消息。

26d172769631233879eb939b91aee52e.png

而且后添加的消息没有任何其他信息。我们在 第 4 条里说过,异常消息应该描述异常事件。然后堆栈跟踪会告诉你抛出异常的是哪个类,那种方法和哪一行。

如果你需要添加更多的信息,应该捕获异常并把它打包在一个自定义的信息中。但请务必遵循第 9 条。

571863f5b6784b98586b1150b3557405.png

所以,只有在你想处理该异常时再捕获它。否则就在方法签名中指明它,让调用者去处理。

9、在不损耗异常的情况下打包它

有时,抓住标准的异常并将其打包到自定义异常中更好。比较典型的是应用程序或框架特定的业务异常,你可以添加其他信息,还可以为异常类执行特殊处理。

这样做的过程中,要确保把原始的异常设置为原因(异常类提供了一个特定的构造函数用来接受可抛出对象作为参数)。不这样做的话,原始异常的堆栈跟踪和消息就会丢失,导致异常的事件难以分析。

860b5ee80458d69d4362318866dc7f8e.png

总结:

如你所见,在你抛出或捕获异常的时候,应该考虑很多不同的事情,其中大多数都是为了提高代码的可读性或 API 的可用性。

异常通常是由一个错误的处理机制和同时使用的沟通媒介导致的。因此,你应该和同事讨论你要用的做法和规则,让每个人都理解通用的概念,然后用相同的方式执行这些做法和规则。

071438f07d22d3369246bdd183e8209f.png

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值