java try的用法_如何正确处理Java异常

Java异常类型结构:

272c1f750bd38eb13eddd33f434c5a51.png
  • Error
    Error 描述了 Java 程序运行时系统的内部错误,通常比较严重,除了通知用户和尽力使应用程序安全地终止之外,其它无能为力,应用程序不应该尝试去捕获这种异常,通常为一些虚拟机异常,如 StackOverflowError、OutOfMemoryError 等等。
  • Exception
    Exception 类型下面又分为两个分支,一个分支派生自 RuntimeException,称为非受检查异常(UncheckedException),这种异常通常为程序错误导致的异常;另一个分支为派生自 IOException 的异常,称为受检查异常(CheckedException),这种异常通常是程序本身没有问题,由于像 I/O 错误等问题导致的异常。

CheckedException和UncheckedException的区别:

CheckedException会在编译时被检测,即要么使用try catch 代码块,要么在方法签名中用 throws 关键字声明该方法可能会抛出的CheckedException,否则编译无法通过。

CheckedException是Java中一个非常糟糕的设计

  1. 它会强迫程序员编写try catch来处理,事实上当出现此类异常时大多数时候程序员也不知道该采取什么措施来处理,这就导致大部分的程序员在catch之后什么也没做,掩盖异常仅仅是为了能编译通过。
  2. 破坏向后兼容性。受检查异常可以在方法签名上标明具体错误原因,但实践中,每次在签名上增加一个新的异常,都会破坏向后兼容性。

第二点还是次要的,最主要的是第一点,在我从事Java编程三年多接触过的实际项目中,目睹了无数的代码胡乱的的try catch,catch后又不throw,或者throw一个其它异常,又不带上原始异常,导致原始异常丢失,或者干脆啥也不做,结果埋下了无数的坑,给以后接手的人带来很多问题。

CheckedException在Java8越来越被标准库所摒弃。Java 7的Files API 用的还是基类IOException,Java 8的Function、BiFunction等压根就不支持CheckedException。Java8标准库中还加入了UncheckedIOException用来避免CheckedException带来的问题。

准确地讲,Java标准库中大部分的CheckedException并不应该定义为CheckedException,因为这些异常和 NullPointerException、 OutOfMemoryError 等一样,除了报错,没什么可特别处理的,也处理不了,除了强迫程序员编写try catch增加工作量之外,并没有其它更多的用处。

例如,我们看看下面的代码,很普遍的写法,大部分人都是try catch之后一句e.printStackTrace()就完事了:

public 

这个InetAddress类的getAllByName方法声明了会抛出一个受检查的异常UnknownHostException,什么情况下会抛出这个异常呢?可能是传入的host有误,可能是网络出现问题,不管是哪种情况,都不是catch之后能解决的。既然不能解决,又何必要强迫程序员去try catch呢,因此这里这个UnknownHostException就不应该定义为CheckedException,但是类库既然已经设计成这样,我们也无法改变,更好的做法是这样:

public 

再看另外一个例子:

public 

这里encode方法会抛出一个UnsupportedEncodingException,这里如果出现这个异常,表明传入的编码有误,在catch里根本什么也做不了,因此强迫强迫程序员去try catch毫无意义。出现了此异常则只能修改传入的编码字段。

如果传入了正确的编码,如UTF-8,我们知道UTF-8是所有平台的JDK都支持的编码,所以这里根本不可能出现UnsupportedEncodingException,因此这个UnsupportedEncodingException定义UncheckedException才是合理的做法。所以说在Java类库API里,有很多CheckedException我们明知道不可能发生,但还是不得不try catch一下,大大增加了代码噪声。

总的来说,Java的CheckedException特性在工程实践中一直是很糟糕的体验,比如我写一个保存当前编辑信息到远程服务器文件的方法saveToRemoteFile,这个方法里有非常复杂的保存逻辑,可能会有n层的子方法调用,而内层的文件API调用可能会抛出各种类型的异常,比如远程文件不存在,磁盘空间不足,权限不足,网络连接异常等等。

所有这些异常对保存逻辑来说全都没法进行处理,通常遇到这些异常内层的子方法完全不必理会,只需要在最外层的saveToRemoteFile里catch住所有的IO异常,然后清理下现场,告知下用户就可以了。整个代码干净整洁,不用担心内层的子方法忘记检查异常从而出现程序失控,同时对异常也进行了良好的处理。

反之,有了CheckedException机制,所有内层函数都必须声明会抛出IO异常,多了很多无谓的声明代码。如果哪个新手在声明方法时忘记了声明异常,然后在写实现代码的时候又遇到了编译错误,于是根据编辑器提示加上了try catch实现以通过编译,在catch里面又什么都不做,结果就导致了忽略异常的严重逻辑错误。

所以CheckedException通常并不能使你的代码变的更安全,反而因为繁琐的声明更容易误导程序员做出危险的操作。所以很多项目干脆直接把异常包装成RuntimeException以规避Check,将CheckedException不分青红皂白包装成RuntimeException抛出,在Java编程圈里已经是一个公开的秘密了吧。

后来Java8 UncheckedIOException 的出现,连官方都鼓励不要CheckedException了,在大名鼎鼎的Spring框架的设计哲学中更是没有CheckedException这个东西。

关于CheckedException的我总结了项目中最普遍的错误用法(下面的logger用的是slf4j类库)

错误用法一:

try 

错误用法二:

try 

错误用法三:

try 

错误用法四:

try 

错误用法五:

try 

正确的做法:

try 

或者

try 

在catch异常之后要如何处理,一定要考虑业务上的可能性,至于是继续抛出还是打印日志后给个默认值要视业务上的要求而定。

例如我们使用SimpleDateFormat来解析字符串到Date对象时,有这么个方法:

public 

另一种情况:

/**

Spring框架中如何正确处理和使用异常

在Spring框架中,Controller和Service不处理任何异常,所有异常由ControllerAdvice统一处理,可以遵循以下实践:

一个异常枚举类,所有异常码定义在这里:

public 

一个业务异常类:

public 

一个全局的异常处理类:

@RestControllerAdvice

其中Result类的定义:

public 

示例Controller类:

@RestController

示例Service类:

@Service

还有一些Java Bean和Mybatis相关类就不贴出来了。

不管是Controller还是Service,只管抛出异常,不做任何处理,由ControllerAdvice统一处理。通过遵循这样的实践,代码变得简洁清晰,没有太多的代码嵌套,同时所有的异常都得到了妥善的处理。

项目github地址:

https://github.com/jufeng98/java-master.git​github.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值