一、异常是什么?
异常的定义:异常是导致一个程序中断的指令流,一旦出现之后程序就立即退出。
例如:除数为0
int a = 10;
int b = 0;
System.out.println(a + "/" + b + "=" + a/b);
System.out.println("运算结束");
程序运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ch1.Test1.main(Test1.java:8)
因此,一旦发生了异常后,程序将不再向下执行,直接退出程序.并输出异常信息。
为了保证程序中即使出现异常了可以处理异常并继续运行,那么就需要使用异常处理语句了。
二、怎么处理异常?
1、在Java中处理异常的语句:
try {
System.out.println(a + "/" + b + "=" + a/b);
//可能出现异常的语句
}catch(异常类型 异常对象){
//在这里处理异常1
}catch(异常类型 异常对象){
//在这里处理异常2
//......可以捕获多个异常对象
} finally {
//异常处理的统一出口,不管是否有异常、是否处理了异常都会执行
}
2、因此可以使用try…catch…finally 语句处理刚刚出现的异常:
int a = 10;
int b = 0;
try {
//可能出现异常的语句
System.out.println(a + "/" + b + "=" + a/b);
}catch(ArithmeticException e){
//在这里处理异常
System.err.println("数据有误");
} finally {
System.out.println("执行了finally块");
}
System.out.println("运算结束");
输出结果为:
数据有误
执行了finally块
运算结束
由于程序中添加了异常处理机制,所以此时的程序可以正常执行完毕,其中可以加入finally语句块作为统一的出口操作(不管是否有异常、是否处理了异常都会执行)。
3、finally用来做什么(作用):
对于没有垃圾回收机制和结构函数自动调用机制(析构函数:当对象不再被使用时会被调用的函数)的语言来说,finally语句非常重要,它能使程序员保证:无论try块里发生了什么,内存总能得到释放。但是Java有垃圾回收机制,所以内存释放不再是问题,另外,java也没有析构函数可供调用。那么在什么情况下才能用到finally呢?
当然是要把除内存之外的资源恢复到出事状态时。这种需要清理的资源包括:已打开的文件或者网络连接,在屏幕上画的图形,甚至可以是外部的某个开关。
四、异常的匹配
抛出异常对象后,异常处理机制会按照catch的顺序匹配出“最近”的异常对象。找到匹配的处理程序之后,异常处理机制就认为异常得到了处理,就不会继续查找(区别于switch,switch需要break进行跳出匹配)。
值得注意的是:
1、查找的时候并不要求抛出的异常对象与处理程序所声明的异常对象完全匹配,派生类的对象也可以匹配其基类(父类)的处理程序。就如:
try {
throw new NumberFormatException();//在此抛出一个异常对象
} catch (Exception e) {
e.printStackTrace();
}
2、编译器不允许把范围大的异常对象放在前面,假如捕获到了Exception异常对象,那么就停止捕获,而作为其子类的处理程序永远得不到执行。就如:
try {
throw new NumberFormatException();
} catch (Exception e) { //这里编译器报错,从精确的异常匹配更大的异常。
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
}
五、throws 与 throw关键字
1、throws关键字作用于方法的声明上,表示一个方法不处理异常,而交由调用处进行处理。
class MyMath {
public int division(int num1, int num2) throws Exception {
return num1/num2;
}
}
public class MyMathTest {
public static void main(String args[]) {
//调用division(),如果不进行异常处理,则编译不能通过(但是有特例)
try {
System.out.println(new MyMath().division(10,0));
} catch(Exception e) {
e.printStackTrace();
}
}
}
throws关键字不仅可以在普通方法上使用,mian方法上也可以使用。即mian方法不对异常进行处理,而向它的上级抛出,即由JVM进行处理。
实际上默认情况下所有异常都是交由JVM进行处理的,比如(一)中。
2、throw关键字:在程序中人为抛出一个异常对象。
try {
throw new Exception("我自己抛的异常");
} catch (Exception e) {
e.printStackTrace();
}
/*
OutPut:
java.lang.Exception: 我自己抛的异常
at ch1.Test1.main(Test1.java:22)
*/
实际使用中较少。
3、throws 与 throw的区别:
六、RuntimeException——“不受检查异常”
在(五、1)的代码中就有注释:如果不进行异常处理,则编译不能通过(但是有特例),现在就来讲这个特例。
引入:如果将一根字符串变为整数类型,我们可以使用Integer类中的parseInt()方法实现。
查看Java API可知parseInt()方法:
public static int parseInt(String s, int radix) throws NumberFormatException
本方法存在了throws关键字的声明,理论上讲,在调用时必须进行异常处理,可实际的使用中可以不使用try…catch处理也可以编译通过。
public class Test {
public static void main(String[] args) {
int a = Integer.parseInt("10");
}
}
要想了解这个问题,就必须观察NumberFormatException异常的继承结构:
可见NumberFormatException是RuntimeException的子类,Java为了异常的处理方便,定义出了一个特殊的异常类——RuntimeException,一旦抛出异常是此类或者此类的子类,那么可以不用进行异常处理。如果不做任何处理,则一旦出现异常后将交由调用处理处进行处理。
RuntimException和Exception的区别:Exception必须处理,RuntimException可以不用处理。
七、异常处理模型
异常处理理论上有两种基本模型:终止模型、恢复模型
1、终止模型:程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已经无法挽回,也不能回来继续执行。
2、恢复模型:意思是在异常处理中修正错误,然后重新尝试调用出问题的代码或者方法,并认为第二次能成功。对于恢复模型,通常系统异常被处理之后能继续执行程序,如果要实现该模型,那么在遇到错误时不能抛出异常,而是调用方法来修正该错误。或者,把try块放在while循环里面,并在try块后添加break退出语句(直到达到满意的结果即退出)。
异常表面上看,恢复模型比终止模型好。但实际上程序员更喜欢使用“终止模型”的代码,而忽略恢复的行为。其中主要原因可能是它所导致的耦合:恢复性的处理程序需要了解异常抛出的地点,这将依赖于抛出位置的非通用性代码,这增加了代码编写和维护的困难,更别说异常可能从许多地方抛出的大型程序。
八、总结:
异常是Java程序设计不可分割的一部分,如果不了解如何使用它们,那你只能完成很有限的工作。我们不应把Java的异常处理机制当成是单一用途的工具。是的,他被设计出来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的。然而,它对于发现某些编译器无法检测到的错误,也是非常重要的。
注:个人知识有限,文章有错误或者不足之处欢迎指正。