一,Java
异常处理及异常机制介绍
当出现程序无法控制的外部环境问题(用户提供的文件不存在,文件内容损坏,
网络不可用
...
)时,
JAVA
就会用异常对象来描述。
JAVA
中用
2
种方法处理异常:
1
在发生异常的地方直接处理;
2
将异常抛给调用者
,
让调用者处理。
JAVA
异常可分为
3
种:
(1)
检查性异常
:java.lang.Exception
(2)
运行期异常
:java.lang.RuntimeException
(3)
错误
:java.lang.Error
顶层是
java.lang.Throwable
类,检查性异常,运行期异常,错误都是这个类的
子孙类。
java.lang.Exception
和
java.lang.Error
继承自
java.lang.Throwable,
而
java.lang.RuntimeException
继承自
java.lang.Exception.
检查性异常
------
程序正确,但因为外在的环境条件不满足引发。
例如:用户错误及
I/O
问题
----
程序试图打开一个并不存在的远程
Socket
端口。这不是程序本身的逻辑错误,而很可能是远程机器名字错误
(
用户拼写错
误
)
。对商用软件系统,程序开发者必须考虑并处理这个问题。
JAVA
编译器强制
要求处理这类异常,如果不捕获这类异常,程序将不能被编译。
运行期异常
------
这意味着程序存在
bug
,如数组越界,
0
被除,入参不满足规范
.....
这
类异常需要更改程序来避免,
JAVA
编译器强制要求处理这类异常。
错误
------
一般很少见,也很难通过程序解决。它可能源于程序的
bug
,但一般更
可能源于环境问题,如内存耗尽。错误在程序中无须处理,而有运行环境处理。
如何处理异常?
1.try...catch
程序运行产生异常时
,
将从异常发生点中断程序并向外抛出异常信息。
Java
代码
int x = (int)(Math.random()*5);
int y = (int)(Math.random()*10);
int[] z =new int[5];
try{
System.out.println("y/x="+(y/x));
System.out.println("y="+y+"z[y]="+z[y]);
} catch (ArithmeticException exc1) {
System.out.println("
算术运算异常
:"+exc1.getMessage());
} catch (ArrayIndexOutOfBoundsException exc2) {
System.out.println("
数据越界异常
:"+exc2.getMessage());
}
说明
:
ArithmeticException(
算术异常
)
和
ArrayIndexOutOfBoundsException(
数
组下标越界异常
)
都属于
java.lang.RuntimeException
(运行期异常),如果不用
try...catch
捕获,程序也是可通过编译的,但如果属于
java.lang.Exception
(检
查性异常),必须而且一定要用
try...catch...
对其进行处理。
2.finally
如果把
finally
块置
try...catch...
语句后,
finally
块一般都会得到执行,它相
当于一个万能的保险,即使前面的
try
块发生异常,而又没有对应异常的
catch
块,
finally
块将马上执行。
以下情形,
finally
块将不会被执行:
(1)finally
块中发生了异常;
(2)
程序所在线程死亡;
(3)
在前面的代码中用了
System.exit()
;
(4)
关闭
CPU
3.
多个异常的处理规则:
定义多个
catch
可精确地定位异常。如果为子类的异常定义了特殊的
catch
块,而父类的异常则放在另外一个
catch
块中,此时,必须满足以下规则:子类
异常的处理块必须在父类异常处理块的前面,否则会发生编译错误。所以,越特
殊的异常越在前面处理,越普遍的异常越在后面处理。这类似于制订防火墙的规
则次序:较特殊的规则在前,较普通的规则在后。
4.
自行抛出异常
在程序中自行抛出异常使用
throw
语句。
throw
语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常
实例。
throw
语句的语法格式:
throw ExceptionInstance;
方法是
2
步:创建异常,抛出异常
(
首先实例化一个异常对象
,
然后用
thow
抛出
)
合在一起就是
----throw new IOException("
异常说明信息
")
。将创建异常,抛出异
常合在一起的好处是:创建异常时,会包含异常创建处的行信息,异常被捕获时
可以通过堆栈迹
(stack Trace)
的形式报告这些信息。如果在同一行代码创建和抛
出异常,对于程序的调试将非常有用。
所以,
thow new XXX()
已经成为一个标准的异常抛出范式。
在定义一个方法时,方法块中调用的方法可能会抛出异常,可用上面的
throw
new XXX()
处理,如果不处理,那么必须在方法定义时,用
thows
声明这个方法
会抛出的异常。
对异常的处理,有一条行之有效的默认规则:向上抛出
-----
被调用类在运行过程
中对遇到的异常一概不作处理,而是直接向上抛出,一直到最上层的调用类,调
用类根据应用系统的需求和特定的异常处理规则进行处理,如向控制台输出异常
堆栈信息,打印在日志文件中。用一句形象的话来说,就是谁使用,谁
(
最上层
的调用类
)
处理。
Question 63
Given the exhibit:
What is the result?
A. test
B. Exception
C. Compilation fails
D. NullPointerException
考点:
异常捕获的顺序
考点说明:
处理异常的一个原则:“先处理小异常,再处理大异常”。
对异常进行捕获时,我们应总是把对
Exception
类的
catch
块放在最后。如
果我们把
Exception
对应的
catch
块排在其他
catch
块的前面,
Java
运行时将直
接进入该
catch
块(因为所有异常对象都是
Exception
或其子类的实例),而排
在它后面的
catch
块将永远也不会获得执行的机会。
本题详解:
Exception
的
catch
块应放在
NullPointerException
的
catch
块后面。
Question 54
Given the exhibit:
What is the
result if NullPointerException occurs on line 34?
A. c
B. a
C. ab
D. ac
E. bc
F. abc
考点:
异常的执行流程
考点说明:
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,
java
虚拟
机检测寻找和
try
关键字匹配的处理该异常的
catch
块,如果找到,将控制权交
到
catch
块中的代码,然后继续往下执行程序,
try
块中发生异常的代码不会被
重新执行。如果没有找到处理该异常的
catch
块,在所有的
finally
块代码被执行
和当前线程的所属的
ThreadGroup
的
uncaughtException
方法被调用后,遇到
异常的当前线程被中止。
finally
关键字保证无论程序使用任何方式离开
try
块,
finally
中的语句都会
被执行。在以下三种情况下会进入
finally
块:
(
1
)
try
块中的代码正常执行完毕。
(
2
) 在
try
块中抛出异常。
(
3
) 在
try
块中执行
return
、
break
、
continue
。
因此,当你需要一个地方来执行在任何情况下都必须执行的代码时,就可以
将这些代码放入
finally
块中。当你的程序中使用了外界资源
,
如数据库连接,文
件等,必须将释放这些资源的代码写入
finally
块中。
本题详解:
在
line34
处产生一个
NullPointerException
,代码会从产生异常的地方跳出,
然后找匹配
NullPointerException
的
catch
块,执行该
catch
块中的代码,输出
a
,执行完之后程序再进入
finally
块,执行
finally
块中的代码,输出
c
。
Exhibit:
Given the exhibit:
25. try{
26.
A a = new A();
27.
a.method1();
28. } catch(Exception e){
29.
System.out.print(“an error occurred”);
30. }
Which two statements are true if a NullPointer Exception is thrown on line 3 of
class C?(choose two)
A. The application will crash.
B. The code on line 29 will be executed
C. The code on line 5 of class A will execute.
D. The code on line 5 of class B will execute.
E. The exception will be propagated back to line 27.
考点:
异常的传播
考点说明:
面向对象的应用程序运行时,经常会发生一系列方法调用,从而形成“方法
调用栈”,异常的传播则相反:只要异常没有被完全捕获(包括异常没有被捕获,
或异常被处理后重新抛出了新异常),异常从发生异常的方法逐渐向外传播,首
先传给该方法的调用者,该方法调用者再次传给其调用者„„直至最后传到
main
方法,如果
main
方法依然没有处理该异常,
JVM
会中止该程序,并打印
异常的跟踪栈信息。
本题详解:
本题中,
C
类的
method3
方法中产生一个异常,向上传播给其调用者
B
类
的
method2
方法,再传播给上一层调用者
A
类的
method1
方法。调用者
a
在调
用
method1
时对异常进行了捕获,并且捕获异常的范围是
Exception
,因此
catch
块中的代码,即
line29
,会得到执行。