一,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 ,会得到执行。