一:
RuntimeException和checked Exception的区别:
1、所有方法都可以在不声明throws的情况下抛出RuntimeException及其子类; 不可以在不声明的情况下抛出非RuntimeException
例如:eg1
---------------------------------------------------------------
1 public void extecTest1(Object o){
2 //if(0==null) throw new MyException();
3 if(o==null) throw new NullPointerException();
4
. try{
FileInputStream fis = new FileInputStream(new File("."));
}catch(IOException e){
throw new RuntimeException(e);
}
}
---------------------------------------------------------------
其中2行注释处如果放开,则通不过编译,除非定义为:eg2
---------------------------------------------------------------
1 public void extecTest1(Object o)throws MyException{
2 if(0==null) throw new MyException();
}
---------------------------------------------------------------
比较两个例子可以比较清楚地看到RuntimeException和自定义Exception在使用上的差别;
Java的异常体系结构:
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。Java异常体系结构呈树状,其层次结构图如图 1所示:
Throwable
Error Exception
RuntimeException 非运行时Exception
二:
一些常见的RuntimeException
Java中采用了强制异常捕捉机制,这样一方面提高了程序的可靠性,不过有时候也带来一些麻烦。比如: int i= Integer.parseInt("33");这个我知道它不可能会抛出异常,但是不捕捉异常的话就过不了编译这关,当然这个代码没有意义的。
Java中所有异常或者错误都继承Throwable,我们把它分为三类吧:
1.Error:所有都继承自Error,表示致命的错误,比如内存不够,字节码不合法等。
2.Exception:这个属于应用程序级别的异常,这类异常必须捕捉。
3.RuntimeException:奇怪RuntimeException继承了Exception,而不是直接继Error, 这个表示系统异常,比较严重。
Error我们很少遇到,但是并不是说Error就一定非常致命,举个例子,NoSuchMethodError表示没有这个方法,你调用的方法不存在,你一定觉得奇怪,不存在怎么编译的过去呀?很简单的,你先编译好一个被调用的类A,给一个方法。然后在你的程序调用它,编译没问题,运行也没问题。现在再把A类中这个方法去掉,重新编译一遍,你再运行你的程序就知道错误是怎么回事了。
Exception不用说,我们要自己捕捉。
RuntimeException可以说见的最多了,下面我们说明一下常见的RuntimeException:
NullPointerException:见的最多了,其实很简单,一般都是在null对象上调用方法了。
String s=null;
boolean eq=s.equals(""); // NullPointerException
这里你看的非常明白了,为什么一到程序中就晕呢?
public int getNumber(String str){
if(str.equals("A")) return 1;
else if(str.equals("B")) return 2;
}
这个方法就有可能抛出NullPointerException,我建议你主动抛出异常,因为代码一多,你可能又晕了。
public int getNumber(String str){
if(str==null) throw new NullPointerException("参数不能为空");
//你是否觉得明白多了
if(str.equals("A")) return 1;
else if(str.equals("B")) return 2;
}
NumberFormatException:继承IllegalArgumentException,字符串转换为数字时。
比如int i= Integer.parseInt("ab3");
ArrayIndexOutOfBoundsException:数组越界
比如 int[] a=new int[3]; int b=a[3];
StringIndexOutOfBoundsException:字符串越界
比如 String s="hello"; char c=s.chatAt(6);
ClassCastException:类型转换错误
比如 Object obj=new Object(); String s=(String)obj;
UnsupportedOperationException:该操作不被支持,如果我们希望不支持这个方法,可以抛出这个异常。既然不支持还要这个干吗?有可能子类中不想支持父类中有的方法,可以直接抛出这个异常。
ArithmeticException:算术错误,典型的就是0作为除数的时候。
IllegalArgumentException:非法参数,在把字符串转换成数字的时候经常出现的一个异常,我们可以在自己的程序中好好利用这个异常。
这些异常一看到名字就知道是怎么回事了,其实只要理解了java的异常处理机制,这些都不是问题。
三.高效的异常处理框架:
1、Error与Exception
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
2、运行时异常和非运行时异常
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
异常处理的原则:
1、 能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。
2、 对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。
3、 对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户
例如一个用户登录的例子,可能发生的异常:
可能产生的异常有: IOException (例如读取配置文件找不到)
SQLException (例如连接数据库错误) ClassNotFoundException(找不到数据库驱动类)
NoSuchUserException PasswordNotMatchException
以上3个异常是和业务逻辑无关的系统容错异常,所以应该转换为RuntimeException,不强制类调用者来处理;而下面两个异常是和业务逻辑相关的流程,从业务实现的角度来说,类调用者必须处理,所以要Checked,强迫调用者去处理。
上述的原则可以进一步细化:
在异常封装时:
第一:如果要创建新的checked异常,尽量包含多一点信息,如果只是一条message,那么用Exception好了。当然,用Exception会失去异常的型别信息,让客户端无法判断具体型别,从而无法针对特定异常进行处理。
第二:不要让你要抛出的checked exception升级到较高的层次。例如,不要让SQLException延伸到业务层。这样可以避免方法签名后有太多的throws。在业务层将持久层的所有异常统统归为业务层自定义的一种异常。
客户端调用时:
不要忽略异常。既然是checked异常,catch clause里理应做些有用的事情??修复它!catch里为空白或者仅仅打印出错信息都是不妥的!为空白就是假装不知道甚至瞒天过海,但是,出来混迟早 要还的,迟早会报unchecked异常并程序挂起!打印出错信息也好不到哪里去,和空白相比除了多几行信息没啥区别。
不要捕获顶层的Exception。因为unchecked异常也是一种Exception,这样做的直接的后果就是,你的程序一般来说是不会挂起了,但是出现错误的时候功能废了,表面上却看不出什么;当然,如果你在catch里打印了信息,这和上面那条的情况是差不多的。
关于异常官方的观点及总结:
第 39 条:最好为异常条件使用异常。也就是说,最好不为控制流使用异常。
第 40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。
第 41 条:避免不必要的使用检查型异常。
第 43 条:抛出与抽象相适应的异常。(使处理异常更直观)
在异常的使用上,专家的观点是很不一样的
C#作者Anders根本就忽略检查型异常。
Bruce Eckel,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
缺点1,代码中包含了过多的catch,使得代码不清晰
缺点2,有时候捕捉的异常没有什么实际意义
缺点3,不够清晰的错误指示。
缺点4,过深的异常层次。
缺点4,性能。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Eckel 提倡将所有的异常都作为非检查型的,并且提供将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力
Rod Johnson ,他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
解决1:谨慎的抛出检查型异常。或者你认为,你可以处理它。否则,包装为运行时异常。
解决2:如果遵守1,2不是问题
解决3:异常不跨层,否则必须捕捉或者包装。
比如持久层丢出的SalException,你或者丢弃/处理/包装(为运行时异常),或者重新包装为业务层异常。保持JEE层的独立和异常的清晰性。
包装底层异常,保持异常链。
解决4:如果符合1,4也不是问题。再次强调,能捕捉就捕捉。
解决5:减少异常使用,减少层次。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
参考资料:http://www.360doc.com/content/10/0526/08/1524832_29555980.shtml
http://wenku.baidu.com/view/6374c411f18583d049645946.html
http://wenku.baidu.com/view/1c50ef00a6c30c2259019e49.html
http://www.tmio.cn/html-104236-1.html