志在指尖
用双手敲打未来

你竟然还在用 try–catch-finally

这是读者Alice上星期特意给我发来的信息,真令我动容。的确,上次的“我去”阅览量杠杠的,几个大号都转载了,包括CSDN,次条当天都1.5万阅览。但比如“还认为你有什么新特技,没想到用的是Java13”这类批评的声响也不在少数。
不过我的心一直很大。从我写榜首篇文章至今,被喷的次数就好像头顶上茂密的发量一样,数也数不清。所以我决定再接再厉,带来新的一篇“我去”。
这次不必长途review了,由于咱们公司也复工了。这次review的代码仍然是小王的,他编写的大部分代码都很美丽,严谨的一起注释也很到位,这令我十分满足。但当我看到他没用try-with-resources时,还是不由得破口大骂:“我擦,小王,你丫的居然还在用try–catch-finally!”
来看看小王写的代码吧。
publicclassTrycatchfinally{
publicstaticvoidmain(String[]args){
BufferedReaderbr=null;
try{
br=newBufferedReader(newFileReader(“/牛逼.txt”));
Stringstr=null;
while((str=br.readLine())!=null){
System.out.println(str);
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
if(br!=null){
try{
br.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}
咦,感觉这段代码很完美无缺啊,try–catch-finally用得中规中矩,尤其是文件名牛逼.txt很亮。不必写注释都能明白这段代码是干嘛的:在try块中读取文件中的内容,并一行一行地打印到控制台。假设文件找不到或者出现IO读写过错,就在catch中捕获并打印过错的仓库信息。最后,在finally中封闭缓冲字符读取器目标BufferedReader,有用杜绝了资源未被封闭的情况下造成的严峻功能成果。
在Java7之前,try–catch-finally的确是保证资源会被及时封闭的最佳办法,无论程序是否会抛出反常。
可是呢,有经历的读者会从上面这段代码中发现2个严峻的问题:
1)文件名“牛逼.txt”包含了中文,需求经过java.net.URLDecoder类的decode()办法对其转义,不然这段代码在运转时铁定要抛出文件找不到的反常。
2)假设直接经过newFileReader(“牛逼.txt”)创立FileReader目标,“牛逼.txt”需求和项目的src在同一级目录下,不然同样会抛出文件找不到的反常。但大多数情况下,(装备)文件会放在resources目录下,便于编译后文件出现在classes目录下,Java
为了处理以上2个问题,咱们需求对代码进行优化:
publicclassTrycatchfinallyDecoder{
publicstaticvoidmain(String[]args){
BufferedReaderbr=null;
try{
Stringpath=TrycatchfinallyDecoder.class.getResource(“/牛逼.txt”).getFile();
StringdecodePath=URLDecoder.decode(path,”utf-8″);
br=newBufferedReader(newFileReader(decodePath));
Stringstr=null;
while((str=br.readLine())!=null){
System.out.println(str);
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
if(br!=null){
try{
br.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}
运转这段代码,程序就能够将文件中的内容正确输出到控制台。但假设你对“整洁”这个词心生神往的话,会感觉这段代码十分臃肿,尤其是finally中的代码,就好像一个灌了12瓶雪花啤酒的大肚腩。
网上看到一幅Python程序员调侃Java程序员的神图,直接copy过来(侵删),逗你一乐:
何况,try–catch-finally至始至终存在一个严峻的危险:try中的br.readLine()有可能会抛出IOException,finally中的br.close()也有可能会抛出IOException。假设两处都不幸地抛出了IOException,那程序的调试使命就变得复杂了起来,到底是哪一处出了过错,就需求花一番功夫,这是咱们不愿意看到的成果。
为了模拟上述情况,咱们来自定义一个类MyfinallyReadLineThrow,它有两个办法,分别是readLine()和close(),办法体都是自动抛出反常。
classMyfinallyReadLineThrow{
publicvoidclose()throwsException{
thrownewException(“close”);
}
publicvoidreadLine()throwsException{
thrownewException(“readLine”);
}
}
然后咱们在main()办法中运用try-finally的方式调用MyfinallyReadLineThrow的readLine()和close()办法:
publicclassTryfinallyCustomReadLineThrow{
publicstaticvoidmain(String[]args)throwsException{
MyfinallyReadLineThrowmyThrow=null;
try{
myThrow=newMyfinallyReadLineThrow();
myThrow.readLine();
}finally{
myThrow.close();
}
}
}
运转上述代码后,过错仓库如下所示:
Exceptioninthread”main”java.lang.Exception:closeatcom.cmower.dzone.trycatchfinally.MyfinallyOutThrow.close(TryfinallyCustomOutThrow.java:17)atcom.cmower.dzone.trycatchfinally.TryfinallyCustomOutThrow.main(TryfinallyCustomOutThrow.java:10)
readLine()办法的反常信息居然被close()办法的仓库信息吃了,这必然会让咱们误认为要调查的目标是close()办法而不是readLine()——虽然它也是应该怀疑的目标。
但自从有了try-with-resources,这些问题就迎刃而解了,只要需求开释的资源(比如BufferedReader)完成了AutoCloseable接口。有了处理方案之后,咱们来对之前的finally代码块进行减肥。
try(BufferedReaderbr=newBufferedReader(newFileReader(decodePath));){
Stringstr=null;
while((str=br.readLine())!=null){
System.out.println(str);
}
}catch(IOExceptione){
e.printStackTrace();
}
你瞧,finally代码块消失了,取而代之的是把要开释的资源写在try后的()中。假设有多个资源(BufferedReader和PrintWriter)需求开释的话,能够直接在()中增加。
try(BufferedReaderbr=newBufferedReader(newFileReader(decodePath));
PrintWriterwriter=newPrintWriter(newFile(writePath))){
Stringstr=null;
while((str=br.readLine())!=null){
writer.print(str);
}
}catch(IOExceptione){
e.printStackTrace();
}
假设你想开释自定义资源的话,只要让它完成AutoCloseable接口,并供给close()办法即可。
publicclassTrywithresourcesCustom{
publicstaticvoidmain(String[]args){
try(MyResourceresource=newMyResource();){
}catch(Exceptione){
e.printStackTrace();
}
}
}classMyResourceimplementsAutoCloseable{
@Overridepublicvoidclose()throwsException{
System.out.println(“封闭自定义资源”);
}
}
代码运转后输出的成果如下所示:
封闭自定义资源
是不是很奇特?咱们在try()中只是new了一个MyResource的目标,其他什么也没干,但偏偏close()办法中的输出句子执行了。想要知道为什么吗?来看看反编译后的字节码吧。
classMyResourceimplementsAutoCloseable{
MyResource(){
}
publicvoidclose()throwsException{
System.out.println(“封闭自定义资源”);
}
}publicclassTrywithresourcesCustom{
publicTrywithresourcesCustom(){
}
publicstaticvoidmain(String[]args){
try{
MyResourceresource=newMyResource();
resource.close();
}catch(Exceptionvar2){
var2.printStackTrace();
}
}
}
咦,编译器居然自动为try-with-resources进行了变身,在try中调用了close()办法。
接下来,咱们在自定义类中再增加一个out()办法,
classMyResourceOutimplementsAutoCloseable{
@Overridepublicvoidclose()throwsException{
System.out.println(“封闭自定义资源”);
}
publicvoidout()throwsException{
System.out.println(“缄默沉静王二,一枚有趣的程序员”);
}
}
这次,咱们在try中调用一下out()办法:
publicclassTrywithresourcesCustomOut{
publicstaticvoidmain(String[]args){
try(MyResourceOutresource=newMyResourceOut();){
resource.out();
}catch(Exceptione){
e.printStackTrace();
}
}
}
再来看一下反编译的字节码:
publicclassTrywithresourcesCustomOut{
publicTrywithresourcesCustomOut(){
}
publicstaticvoidmain(String[]args){
try{
MyResourceOutresource=newMyResourceOut();
try{
resource.out();
}catch(Throwablevar5){
try{
resource.close();
}catch(Throwablevar4){
var5.addSuppressed(var4);
}
throwvar5;
}
resource.close();
}catch(Exceptionvar6){
var6.printStackTrace();
}
}
}
这次,catch块中自动调用了resource.close(),而且有一段很要害的代码var5.addSuppressed(var4)。它有什么用途呢?当一个反常被抛出的时分,可能有其他反常由于该反常而被按捺住,从而无法正常抛出。这时能够经过addSuppressed()办法把这些被按捺的办法记录下来。被按捺的反常会出现在抛出的反常的仓库信息中,也能够经过getSuppressed()办法来获取这些反常。这样做的好处是不会丢失任何反常,方便咱们开发人员进行调试。
哇,有没有想到咱们之前的那个比如——在try-finally中,readLine()办法的反常信息居然被close()办法的仓库信息吃了。现在有了try-with-resources,再来看看作用和readLine()办法一致的out()办法会不会被close()吃掉。
在close()和out()办法中直接抛出反常:
classMyResourceOutThrowimplementsAutoCloseable{
@Overridepublicvoidclose()throwsException{
thrownewException(“close()”);
}
publicvoidout()throwsException{
thrownewException(“out()”);
}
}
调用这2个办法:
publicclassTrywithresourcesCustomOutThrow{
publicstaticvoidmain(String[]args){
try(MyResourceOutThrowresource=newMyResourceOutThrow();){
resource.out();
}catch(Exceptione){
e.printStackTrace();
}
}
}
程序输出的成果如下所示:
java.lang.Exception:out()
atcom.cmower.dzone.trycatchfinally.MyResourceOutThrow.out(TrywithresourcesCustomOutThrow.java:20)atcom.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:6)Suppressed:java.lang.Exception:close()
atcom.cmower.dzone.trycatchfinally.MyResourceOutThrow.close(TrywithresourcesCustomOutThrow.java:16)atcom.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:5)
瞧,这次不会了,out()的反常仓库信息打印出来了,而且close()办法的仓库信息上加了一个要害字Suppressed。一望而知,不错不错,我喜欢。
总结一下,在处理必须封闭的资源时,一直有限考虑运用try-with-resources,而不是try–catch-finally。前者发生的代码更加简练、清晰,发生的反常信息也更靠谱。答应我好不好?别再用try–catch-finally了。

未经允许不得转载:IT技术网站 » 你竟然还在用 try–catch-finally
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载