异常
现有的 程序开发技术和测试技术无法确保程序中不存在错误,很多错误不是程序编译时出现(也就是代码错误),而是在运行时由于各种原因(突然断网、文件不存在了)产生的。
几个概念
编译错误:编译阶段语法检查,代码书写不满足语法规则。
运行时错误:指令执行条件的错误,语义错误。
逻辑错误:设计不满足要求,未能得到期望结果。
异常:程序在运行时出现的错误。(这里的运行时错误这一概念泛指运行期间发生的错误,与java的RuntimeException类所涵盖的范畴不同,后者仅仅是前者的一个子集)
异常分类:(Java提供了Throwable类,只有Throwable类或其子类的对象,才能被 虚拟机或throw语句抛出,并被catch语句捕获;Throwable类有两个子类:Error和Exception刻画了不同程度的异常)
Error:比较严重的灾难性错误(不可捕获,任其报错)。如:OutOfMemoryError(内存不足)、ThreadDeath、IOError、LinkageError(库链接错误)等。这些异常发生时,JVM一般会选择线程终止。
Exception:程序本身可以处理的异常(可捕获)。分为以下两种
运行时异常:RuntimeException类及其子类异常,JVM正常操作期间抛出的异常,不需要事先声明。可不捕获,JVM会接管(RuntimeExceptionand its subclasses are uncheckedexceptions.Unchecked exceptions donotneed to be declared in a method or constructor'sthrowsclause if they can be thrown by the execution of the method orconstructor and propagate outside the method or constructorboundary.)如:ArithmeticException(算术异常)、BufferOverflowException(缓冲区上益)、BufferUnderflowException、NullPointerException(空指针)。
非运行时异常:RuntimeException以外的异常。必须用catch子句或throws子句指明可能抛出何种异常,否则编译不通过。如:IOException(IO异常)、AWTException(AWT异常)、InterruptedException(中断异常)、TimeoutException(超市异常)
这是别人博客上写的,感觉很好:(出现运行时异常后,系统会把异常一直往上层抛,一直遇到处理代码。如果没有处理块,到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是Exception的子类,也有一般异常的特点,是可以被Catch块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。如果不想终止,则必须扑捉所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。在这个场景这样处理可能是一个比较好的应用,但并不代表在所有的场景你都应该如此。如果在其它场景,遇到了一些错误,如果退出程序比较好,这时你就可以不太理会运行时异常,或者是通过对异常的处理显式的控制程序退出。)
Java异常处理机制
java将程序中运行时出现的各种错误统一处理成“异常对象”,冰箱外抛出;
捕获并处理这个异常对象,处理策略是:处理或 声明
处理:用try-catch机制实现;用try监控可能出现异常的代码段,用catch捕获和处理异常;
声明:就是声明自己不处理,将异常对象抛给它的调用者,用throws机制实现(在方法声明部分用throws指明本方法可能抛出的异常,以提醒调用者务必采取相应的措施)
(借助JVM的支持,java的异常处理与传统处理方式有很大不同。由于字节码在执行前需要由虚拟机将其翻译成二进制代码,然后执行。JVM可以在执行前根据当时状况对执行效果进行预测。若是发生异常,则由虚拟机创建异常对象并抛出)
JVM的工作模式:监控模式和 正常模式
JVM对执行结果预测将会一定程度上影响代码执行效率,因此JVM只对try-catch-finally语句和throws标记的代码段实施监控预测模式。正常模式下,发生的错误只能被Java虚拟机捕获和处理(比如Error和没有捕获的运行时异常哦,将会终止JVM运行)
异常的声明(throws)和外抛(throw)
格式
返回类型 方法名(参数列表) throws 异常列表 {方法体}
throw异常对象的引用
实例
[java]
import java.io.IOException;
import java.util.Scanner;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
f();
} catch (ArrayIndexOutOfBoundsException e) {
// TODO Auto-generated catch block
System.out.println("数组下标越界异常");
}catch (ArithmeticException e) {
// TODO: handle exception
System.out.println("算术异常");
}catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage()+"异常");
}
}
public static void f() throws ArithmeticException,ArrayIndexOutOfBoundsException,Exception{
int[] x={1,2};
int y,z;
while(true){
System.out.print("请输入一个整数,-1表示结束:");
Scanner in=new Scanner(System.in);
y=in.nextInt();
if(y==-1) break;
if(y==2) throw new IOException(); // 假如没有声明Exception的话,这里就会编译错误
z=x[y]/y;
}
}
}
异常传播
来看段代码:
[java]
public class Test {
public static void main(String[] args) {
a();
}
static void a(){
b();
}
static void b(){
c();
}
static void c(){
int i=5/0;
}
}
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at test.Test.c(Test.java:36)
at test.Test.b(Test.java:33)
at test.Test.a(Test.java:30)
at test.Test.main(Test.java:27)
方法调用顺序:
main → a → b → c
异常传递顺序:
c → b → a → main → JVM(JVM终止)
由于ArithmeticException是运行时异常不需要捕获。
注意点
1. 当try语句块中有return语句,finally也将执行
[java]
int f(){
try{
return 5;
}finally{
return 10;
}
}//此方法返回的是10
2.throw有类似return的强力转向功能,即throw后的语句不执行,并且导致编译无法通过。
3. 重写方法中www.2cto.com
[java]
class A{ // 假如这两个异常调换,会报B类中的异常不能跟A类中的兼容
void f() throws Exception{
}
}
class B extends A{ // 也就是说这个方法中的异常必须和上面方法中的相同或是其子类
void f() throws IOException{
}
}
|