1、异常吞噬
看下面的代码
class OneException extends Exception {
public OneException(String s) {
super(s);
}
}
class TwoException extends Exception {
public TwoException(String s) {
super(s);
}
}
class RethrowNew {
public static void throwsOneException() throws OneException {
throw new OneException("OneException");
}
public static void catchOneThrowTwo() throws TwoException {
try {
throwsOneException();
} catch (OneException e) {
//catch异常之后,抛出一个新的异常TwoException,则外部再catch之后无法获取OneException的异常信息,只能获取TwoException
TwoException two = new TwoException("TwoException");
throw two;
}
}
public static void main(String[] args) {
try {
catchOneThrowTwo();
} catch (TwoException e) {
e.printStackTrace(System.out);
}
}
}
定义OneException 和TwoException,捕获OneException后抛出TwoException。
控制台打印如下:
com.thinkinginjava.TwoException: TwoException
at com.thinkinginjava.RethrowNew.catchOneThrowTwo(GG_Exception.java:68) //仅仅定位到catchOneThrowTwo这方法
at com.thinkinginjava.RethrowNew.main(GG_Exception.java:75)
从异常中堆栈中可以看出,堆栈信息仅仅定位到了catchOneThrowTwo方法,而throwsOneException方法抛出的OneException被吞噬了。
可以用 上层异常.initCause(底层异常) 这种方式把 上层异常 和 底层异常 连接起来。code like this
class OneException extends Exception {
public OneException(String s) {
super(s);
}
}
class TwoException extends Exception {
public TwoException(String s) {
super(s);
}
}
class RethrowNew {
public static void throwsOneException() throws OneException {
throw new OneException("OneException");
}
public static void catchOneThrowTwo() throws TwoException {
try {
throwsOneException();
} catch (OneException e) {
TwoException two = new TwoException("TwoException");
//把two和e这两个异常连接起来
two.initCause(e);
throw two;
}
}
public static void main(String[] args) {
try {
catchOneThrowTwo();
} catch (TwoException e) {
e.printStackTrace(System.out);
}
}
}
控制台打印如下:
com.thinkinginjava.TwoException: TwoException
at com.thinkinginjava.RethrowNew.catchOneThrowTwo(GG_Exception.java:67)
at com.thinkinginjava.RethrowNew.main(GG_Exception.java:77)
Caused by: com.thinkinginjava.OneException: OneException //OneException 被打印出来了
at com.thinkinginjava.RethrowNew.throwsOneException(GG_Exception.java:60)
at com.thinkinginjava.RethrowNew.catchOneThrowTwo(GG_Exception.java:65)
... 1 more
2、不受检查异常、受检查异常
RuntimeException及其子类为不受检查的异常。
为什么程序员不需要捕获、抛出这类异常呢?因为虚拟机会自动抛出这类异常,不用程序员在调用链上一直抛出去。补充:RuntimeException是Exception的子类。
class SubRuntimeException extends RuntimeException{
public SubRuntimeException(String s){
super(s);
}
}
class SubException extends Exception{
public SubException(String s){
super(s);
}
}
class CheckNoCheckException {
//RuntimeException为:不受检查异常。可以不catch、不抛出
public static void noCheckFn(){
throw new SubRuntimeException("可以不catch、不抛出");
}
//非RuntimeException的异常为:被检查的异常。要求程序员catch 或者 往上抛出
public static void checkFn() throws SubException{
throw new SubException("需要catch或抛出");
}
public static void main(String[] args) {
try {
checkFn();
}catch (SubException e){
e.printStackTrace(System.out);
}
System.out.println("**********catch异常后继续执行**********");
noCheckFn();
}
}
控制台输出:
com.thinkinginjava.SubException: 需要catch或抛出
at com.thinkinginjava.CheckNoCheckException.checkFn(GG_Exception.java:111)
at com.thinkinginjava.CheckNoCheckException.main(GG_Exception.java:117)
**********catch异常后继续执行**********
Exception in thread "main" com.thinkinginjava.SubRuntimeException: 可以不catch、不抛出
at com.thinkinginjava.CheckNoCheckException.noCheckFn(GG_Exception.java:106)
at com.thinkinginjava.CheckNoCheckException.main(GG_Exception.java:124)
Think in Java的作者并不完全坚信“被检查的异常和强静态类型检查对开发健壮的程序是非常必要的”。
以我的工作经验来看,项目中的常用的业务异常ParamException、ProjectNameException、ModuleNameException继承RuntimeException,使用RuntimeException类型的自定义异常确实能让代码清爽一些,毕竟很多时候catch到一个异常后的处理就是返回“XX失败”、“服务异常”这种信息给前端。
3、评价异常
Think in Java的作者给出这样一个观点:
我一直相信“报告”功能是异常的精髓所在,Java坚定地强调将所有的错误都以异常的形式报告的这一事实,正式它远远超过诸如C++这类语言的长处之一。
这个“统一的报告功能”在代码中的体现之一就是:使用全局异常处理器,处理未被catch的异常,然后返回message = StringUtils.isNotEmpty(exception.getMessage()) ? exception.getMessage() : "服务暂不可用"; 给前端