在处理异常时,应该区分
checked
异常和
unchecked
异常。对于
checked
异常,我们应该提供健壮的异常恢复机制,而对于
unchecked
异常,这就是编程错误即
bug
,应该在调试阶段很好的发现和处理它们。
1. Java
异常层次结构
上图(注:该图引自
[url]http://dev2dev.bea.com.cn/techdoc/200702364792.html[/url]
)标出了
Java
异常层次结构,也指出了哪些异常是
unchecked
,哪些异常是
checked
。下面给出几段常见的异常处理代理,试图总结日常开发中应该如何处理异常。
2.
针对
checked
异常的恢复机制
checked
异常并不是编程错误,它的出现是软件运行阶段所不可避免的。最常见的这类异常如
socket
连接超时。
对于此类异常,我们应该在程序的运行态下试图从异常中恢复过来。下面这段代码
(Recover.java)
的主要逻辑是,对目标值
protected
int
current
进行判断,如果该值大于
2
则成功,否则抛出
NotBigEnoughException
异常。
在执行程序的过程中,在每次
catch
到
NotBigEnoughException
异常时,我们对
current
值递增,试图从异常中恢复过来。
NotBigEnoughException.java
package
com.zj.exception.types;
public
class
NotBigEnoughException
extends
Exception {
public
NotBigEnoughException() {
super
();
}
public
NotBigEnoughException(String msg) {
super
(msg);
}
}
|
Recover.java
package
com.zj.exception;
import
com.zj.exception.types.NotBigEnoughException;
public
class
Recover {
protected
int
current
= 0;
protected
boolean
accept
=
false
;
public
Recover() {}
public
Recover(
int
cur) {
current
= cur;
}
public
void
increment() {
++
current
;
}
public
boolean
passed() {
return
accept
;
}
public
void
passing()
throws
NotBigEnoughException {
if
(
current
> 2) {
accept
=
true
;
System.
out
.println(
"accept "
+
current
);
}
else
throw
new
NotBigEnoughException(
"reject "
+
current
);
}
public
static
void
main(String[] args) {
Recover re =
new
Recover();
while
(!re.passed()) {
try
{
re.passing();
}
catch
(NotBigEnoughException e) {
System.
out
.println(e);
re.increment();
}
}
}
}
|
结果:
com.zj.exception.types.NotBigEnoughException
: reject 0
com.zj.exception.types.NotBigEnoughException
: reject 1
com.zj.exception.types.NotBigEnoughException
: reject 2
accept 3
3.
继承异常
在子类继承父类的情况下,子类
override
方法的异常声明只能取自(小于等于)父类该方法的异常声明;对于子类构造方法的异常声明必须包含(大于等于)父类构造方法的异常声明。
类
Inheritor
继承自类
Recover
,
它的方法
passing()
试图声明一个父类没有的异常
UnknowException
,
这样做是不允许的。
UnknowException.java
package
com.zj.exception.types;
public
class
UnknowException
extends
Exception {
public
UnknowException() {
super
();
}
public
UnknowException(String msg) {
super
(msg);
}
}
|
error in:
Inheritor.java
//couldn't throws new exceptions where not found in its base class
public
void
passing()
throws
NotBigEnoughException, UnknowException {
if
(
current
> 2) {
accept
=
true
;
System.
out
.println(
"accept "
+
current
);
}
else
if
(
current
>= 0)
throw
new
NotBigEnoughException(
"reject "
+
current
);
else
throw
new
UnknowException(
"i don't know how to deal with "
+
current
);
}
|
之所以覆盖这个方法的目的是对父类的
passing()
方法做进一步扩展,对
0<=current<=2
的情况抛出
NotBigEnoughException
,
而对
current<0
的情况则抛出一个新的异常
UnknowException
。
此时,提供两种解决方法。
方法一,使用恢复异常机制,
overrding passing
()方法,这样可以处理掉所有的异常,因此不需要异常声明。
ok in:
Inheritor.java
//sure passing(),so not have to throw exceptions
public
void
passing(){
while
(!passed()) {
try
{
super
.passing();
}
catch
(NotBigEnoughException e) {
increment();
}
}
}
|
方法二,写一个加强的
passing
()方法,即
fortifiedPassing()
,
对于在父类
passing
()中捕获的异常,进行再判断。如果是
0<=current<=2
的情况则重新抛出
NotBigEnoughException
,如果是
current<0
的情况则抛出一个新的异常
UnknowException
。
ok in:
Inheritor.java
public
void
fortifiedPassing()
throws
NotBigEnoughException, UnknowException{
try
{
super
.passing();
}
catch
(NotBigEnoughException e) {
if
(
current
>=0)
throw
e;
else
throw
new
UnknowException(
"i don't know how to deal with "
+
current
);
}
}
|
Inheritor.java
package
com.zj.exception;
import
com.zj.exception.types.NotBigEnoughException;
import
com.zj.exception.types.UnknowException;
public
class
Inheritor
extends
Recover {
public
Inheritor(
int
cur) {
super
(cur);
}
//couldn't throws new exceptions where not found in its base class
/**
public
void
passing()
throws
NotBigEnoughException,
UnknowException
{
if
(current
>
2)
{
accept
=
true;
System.out.println("accept
"
+
current);
}
else
if
(current
>=
0)
throw
new
NotBigEnoughException("reject
"
+
current);
else
throw
new
UnknowException("i
don't
know
how
to
deal
with
"
+
current);
}*/
//sure passing(),so not have to throw exceptions
public
void
passing(){
while
(!passed()) {
try
{
super
.passing();
}
catch
(NotBigEnoughException e) {
increment();
}
}
}
public
void
fortifiedPassing()
throws
NotBigEnoughException, UnknowException{
try
{
super
.passing();
}
catch
(NotBigEnoughException e) {
if
(
current
>=0)
throw
e;
else
throw
new
UnknowException(
"i don't know how to deal with "
+
current
);
}
}
public
static
void
main(String[] args) {
// not required try-catch
new
Inheritor(3).passing();
new
Inheritor(1).passing();
new
Inheritor(-1).passing();
//no exceptions
try
{
new
Inheritor(3).fortifiedPassing();
}
catch
(NotBigEnoughException e) {
e.printStackTrace();
}
catch
(UnknowException e) {
System.
out
.println(e);
}
//NotBigEnoughException:
try
{
new
Inheritor(1).fortifiedPassing();
}
catch
(NotBigEnoughException e) {
e.printStackTrace();
}
catch
(UnknowException e) {
System.
out
.println(e);
}
//UnknownException:
try
{
new
Inheritor(-1).fortifiedPassing();
}
catch
(NotBigEnoughException e) {
e.printStackTrace();
}
catch
(UnknowException e) {
System.
out
.println(e);
}
}
}
|
结果:
accept 3
accept 3
accept 3
accept 3
com.zj.exception.types.UnknowException
: i don't know how to deal with -1
com.zj.exception.types.NotBigEnoughException
: reject 1
at com.zj.exception.Recover.passing(
Recover.java:28
)
at com.zj.exception.Inheritor.fortifiedPassing(
Inheritor.java:38
)
at com.zj.exception.Inheritor.main(
Inheritor.java:63
)
4.RuntimeException
与包装异常
RuntimeException
是
unhecked
异常,它们由
JVM
抛出(你也可以抛出它),并且不必在异常声明(
throws
)中列出。
如果
RuntimeException
没有被
catch
而到达
mian
()方法时,那么在程序退出前会自动调用该异常的
printStackTrace()
方法,打印该异常。
RuntimeException
代表的是编程错误(如
0
除数,数组越界),是应该在调试阶段解决的。
当你在捕获某些异常,而不知道该如果处理时,你可以将它包装为
RuntimeException
,这样在后续的方法调用过程中就不用声明
(throws)
该方法了。
在类
Wrapper
中,我们
override
fortifiedPassing()
方法,并将它可能抛出的异常包装为
RuntimeException
。
Wrapper.java
package
com.zj.exception;
import
com.zj.exception.types.NotBigEnoughException;
import
com.zj.exception.types.UnknowException;
public
class
Wrapper
extends
Inheritor {
public
Wrapper(
int
cur) {
super
(cur);
}
public
void
fortifiedPassing() {
try
{
super
.fortifiedPassing();
}
catch
(NotBigEnoughException e) {
throw
new
RuntimeException(e);
}
catch
(UnknowException e) {
throw
new
RuntimeException(e);
}
}
public
static
void
main(String[] args) {
// not required try-catch
new
Wrapper(3).fortifiedPassing();
new
Wrapper(1).fortifiedPassing();
new
Wrapper(-1).fortifiedPassing();
}
}
|
结果:
accept 3
Exception in thread "main"
java.lang.RuntimeException
:
com.zj.exception.types.NotBigEnoughException
: reject 1
at com.zj.exception.Wrapper.fortifiedPassing(
Wrapper.java:14
)
at com.zj.exception.Wrapper.main(
Wrapper.java:23
)
Caused by:
com.zj.exception.types.NotBigEnoughException
: reject 1
at com.zj.exception.Recover.passing(
Recover.java:28
)
at com.zj.exception.Inheritor.fortifiedPassing(
Inheritor.java:38
)
at com.zj.exception.Wrapper.fortifiedPassing(
Wrapper.java:12
)
... 1 more