当前位置: 首页 > 后端技术 > Java

Java处理异常的9个最佳实践,你做对了吗?..

时间:2023-04-01 20:54:21 Java

在Java中处理异常不是一件简单的事情。不仅初学者难以理解,即使是一些有经验的开发人员也需要花大量时间思考如何处理异常,包括哪些异常需要处理,如何处理等等。这也是为什么大多数开发团队都会制定一些规则来规范异常的处理。这些规范在不同团队之间往往截然不同。本文介绍了许多团队使用的一些异常处理最佳实践。1.清理Finally块中的资源或使用try-with-resource语句当使用像InputStream这样需要在使用后关闭的资源时,一个常见的错误是在try块的末尾关闭资源。publicvoiddoNotCloseResourceInTry(){FileInputStreaminputStream=null;尝试{Filefile=newFile("./tmp.txt");inputStream=newFileInputStream(文件);//使用inputStream读取文件//不要这样做inputStream.close();}catch(FileNotFoundExceptione){log.error(e);}catch(IOExceptione){log.error(e);}}上面的代码运行起来没有任何异常是没有问题的。但是当try块中的语句抛出异常或者自己实现的代码抛出异常时,那么最后的关闭语句将不会被执行,所以无法释放资源。一种合理的方法是将所有清理代码放在finally块中或使用try-with-resource语句。publicvoidcloseResourceInFinally(){FileInputStreaminputStream=null;尝试{Filefile=newFile("./tmp.txt");inputStream=newFileInputStream(文件);//使用输入流读取文件}catch(FileNotFoundExceptione){log.error(e);}finally{if(inputStream!=null){try{inputStream.close();}catch(IOExceptione){log.error(e);}}}}publicvoidautomaticallyCloseResource(){Filefile=newFile("./tmp.txt");try(FileInputStreaminputStream=newFileInputStream(file);){//使用inputStream读取文件}catch(FileNotFoundExceptione){log.error(e);}catch(IOExceptione){log.error(e);}}2.指定一个具体的异常尽可能使用最具体的异常来声明方法,这样可以使代码更容易理解。publicvoiddoNotDoThis()throwsException{...}publicvoiddoThis()throwsNumberFormatException{...}如上,NumberFormatException字面上可以看成是数字格式错误。3.异常的文档当在方法上声明异常时,也需要文档。和上一点一样,都是为了给调用者提供尽可能多的信息,这样才能更好的避免/处理异常。在javadoc中添加throws语句,描述抛出异常的场景。/***这个方法做了一些非常有用的事情...**@paraminput*@throwsMyBusinessExceptionif...happens*/publicvoiddoSomething(Stringinput)throwsMyBusinessException{...}4.当抛出异常时包括描述信息当抛出异常时,需要尽可能准确地描述问题和相关信息,这样无论是打印在日志中还是在监控工具中,都可以更容易阅读,以便具体错误信息可以更好的定位,错误的严重程度等等。但这并不是说错误信息要长篇大论,因为Exception的类名可以反映错误的原因,所以它只需要用一两句话来描述。try{newLong("xyz");}catch(NumberFormatExceptione){log.error(e);}NumberFormatException告诉异常是格式化错误,异常的额外信息只需要提供错误字符串即可。当异常的名称不够明显时,需要尽可能具体地提供错误信息。5.首先捕获最具体的异常现在很多IDE都可以智能提示这个最佳实践。当你尝试先捕获最一般的异常时,它会提示无法到达的代码。当有多个catch块时,按照捕获顺序只执行第一个匹配的catch块。因此,如果先捕获IllegalArgumentException,则无法运行NumberFormatException的捕获。publicvoidcatchMostSpecificExceptionFirst(){try{doSomething("一条消息");}catch(NumberFormatExceptione){log.error(e);}catch(IllegalArgumentExceptione){log.error(e)}}6.不要捕获ThrowableThrowable是所有异常和错误的父类。您可以在catch语句中捕获,但永远不要那样做。如果捕获了throwable,那么不仅所有的异常都会被捕获,错误也会被捕获。error是不可恢复的jvm错误。因此,除非你绝对确定你可以处理或被要求处理错误,否则不要捕获throwable。publicvoiddoNotCatchThrowable(){try{//做点什么}catch(Throwablet){//不要这样做!}}7.不要忽略异常很多时候,开发人员有信心不会抛出异常,所以写一个catch块,但不做任何处理或日志记录。publicvoiddoNotIgnoreExceptions(){try{//dosomething}catch(NumberFormatExceptione){//这永远不会发生}}但现实情况是经常会出现意想不到的异常或者无法确定这里的代码是否会在future(delete是为了防止异常被抛出),并且由于此时捕获了异常,所以无法获得足够的错误信息来定位问题。至少记录异常信息是合理的。publicvoidlogAnException(){try{//做某事}catch(NumberFormatExceptione){log.error("这不应该发生:"+e);}}8.不记录异常并抛出异常可以找到很多代码甚至类库中都会有捕获异常、记录异常并再次抛出的逻辑。如下:try{newLong("xyz");}catch(NumberFormatExceptione){log.error(e);throwe;}这个处理逻辑看似合理。但是这往往会针对同一个异常输出多个日志。如下:17:44:28,945ERRORTestExceptionHandling:65-java.lang.NumberFormatException:Forinputstring:"xyz"Exceptioninthread"main"java.lang.NumberFormatException:Forinputstring:"xyz"atjava.lang.NumberFormatException.forInputString(NumberFormatException.java:65)在java.lang.Long.parseLong(Long.java:589)在java.lang.Long.(Long.java:965)在com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)atcom.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)如上所示,后面的日志没有附加更多有用的信息。如果您想提供更多有用的信息,您可以将异常包装为自定义异常。publicvoidwrapException(Stringinput)throwsMyBusinessException{try{//做某事}catch(NumberFormatExceptione){thrownewMyBusinessException("描述错误的消息。",e);}}因此,只有当想处理异常时才捕获,否则只需要在方法签名中声明,让调用者处理即可。9.打包异常时不要抛弃原有的异常捕获标准异常并打包为自定义异常是一种很常见的做法。这样可以增加更具体的异常信息,有针对性地进行异常处理。需要注意的是,在封装异常时,必须将原来的异常设置为cause(Exception有一个构造函数可以传递给cause)。否则,丢失原始的异常信息将导致错误分析变得困难。publicvoidwrapException(Stringinput)throwsMyBusinessException{try{//做某事}catch(NumberFormatExceptione){thrownewMyBusinessException("描述错误的消息。",e);或者在捕获异常时,需要考虑许多不同的事情。其中很多点都是为了提高代码的可读性或者API的易用性。异常不仅仅是一种错误控制机制,也是一种沟通媒介,所以与你的合作者讨论这些最佳实践并制定一些规范将使每个人都能理解所涉及的共同概念并以相同的方式使用它们。原文:https://dzone.com/articles/9-...译者:SaranHang翻译:http://www.rowkey.me/blog/201...