1.异常概述
在Java中,异常就是在程序运行时产生的错误,例如,向一个不存在的文本文件写入数据时,就会产FileNotFoundException异常(系统找不到指定文件)。接下来通过一个简单的实例认识一下另外一个异常ArithmeticException算数异常。
以3除以0为例,在算数当初0是不能被除的,代码如下:
public class Demo{
public static void main(String[] args) {
int i = 3/0;
System.out.println(i);
}
}
返回结果:
从返回结果可以看出,产生了算术异常ArithmeticException,导致算术异常的根源在于算术表达式3/0中,0作为除数出现,所以正在执行的程序被中断(第3行以后,包括第3行的代码都不会执行)。
除FileNotFoundException异常和ArithmeticException异常外,Java中还有许许多多的异常,如空指针异常、数组元素下标越界异常等等,由于Java是一门面向对象的编程语言,所以在Java中,把上述异常都被称为异常对象,当程序执行到某一方法处产生异常时,java虚拟机(JVM)就会产生与已产生的异常相匹配的异常对象,如果没有异常对象做异常处理,那么久会显示上图所示的异常信息
2.捕捉处理异常
try-catch代码块主要用于捕捉并处理异常,实际应用时,改代码块还有一个可选的finally代码块。try-catch-finally的语法如下:
try {
//程序代码块
}catch(ArithmeticException e ) {
//对ArithmeticException异常处理
}finally {
//代码块
}
- try中的程序代码块指的是可能产生异常的代码;
- catch中的对ArithmeticException的处理的作用是捕捉并处理与已产生的异常类型相匹配的异常对象e;
- finally中的代码块是异常处理过程中最后被执行的部分,无论程序是否产生异常,finally中的代码块都被执行,实际应用时,finally中通常放置一些释放资源、关闭对象的代码;
通过try-catch-finally的语法可知,捕捉处理异常分为try-catch代码块和finally代码块两部分组成,下面分别予以介绍。
2.1try-catch代码块
把可能产生异常的代码放在try中,把处理异常对象e的代码放在catch中,接下来通过上面讲的实例演示一下try-catch代码块,代码如下:
public class Demo{
public static void main(String[] args) {
try {
int i = 3/0;
}catch(ArithmeticException e ) { //对ArithmeticException异常捕捉
e.printStackTrace(); //打印异常
System.out.println("算术发现异常,请检查"); //对发现的异常做处理
}
System.out.println("程序执行完毕");
}
}
返回结果:
从返回结果看出,程序仍然输出最后的提示信息程序执行完毕,这说明程序的执行没有因为产生异常被中断,由此可知,使用try-catch代码块捕捉并处理异常,不会因为产生异常影响程序的执行。
在catch代码块中使用了ArithmeticException对象的printStackTrace()方法输出了异常信息,除此之外,ArithmeticException对象还提供了其他的方法用于获取异常的相关信息。
- getMessage()方法:获取有关异常事件的信息;
- toString()方法:获取异常的类型与性质;
注意:ArithmeticException是try代码块传递给catch代码块的异常类型,e是与已产生的异常类型相匹配的异常对象
在上面例子中,try代码块后面用了一个catch代码块来捕捉异常,但是如果遇到需要处理多种异常信息的情况时,可以在一个try代码块后面跟多个catch代码块,这里需要注意的是,如果使用了多个catch代码块,则catch代码块中的异常类顺序是先子类后父类。
多个catch语法如下:
try {
//程序代码块
}catch() {
//异常处理
}catch() {
//异常处理
}catch() {
//异常处理
}
............
下面我们来捕捉两个异常,一个数组元素下标越界异常和空指针异常,ArrayIndexOutOfBoundsException(数组下标越界异常)NullPointerException(空指针异常),代码如下:
public class Demo{
public static void main(String[] args) {
try {
//创建一个空对象
Object a = null;
//创建一个数组下标为2
int i[] = new int[2];
//输出空对象
a.hashCode();
//输出数组下标为3
System.out.println(i[3]);
//数组越界异常
}catch(ArrayIndexOutOfBoundsException e ) {
System.out.println("发现异常:数组下标越界");
//数组空指针异常
}catch(NullPointerException e ) {
System.out.println("发现异常:空指针");
}
}
}
如果我们把Exception异常放在空指针和数组下标异常前面呢?看下图:
因为Exception是ArrayIndexOutOfBoundsException(数组下标越界异常)NullPointerException(空指针异常)他俩的父类,如果try代码块中有多个catch代码块,顺序应该先子类后父类,所以把父类放在子类就会报错
2.2finally代码块
完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常产生,finally代码块中的代码都会被执行。、
续上面的例子,代码如下:
public class Demo{
public static void main(String[] args) {
try {
//创建一个空对象
Object a = null;
//创建一个数组下标为2
int i[] = new int[2];
//输出空对象
a.hashCode();
//输出数组下标为3
//System.out.println(i[3]);
//数组越界异常
}catch(ArrayIndexOutOfBoundsException e ) {
System.out.println("发现异常:数组下标越界");
//数组空指针异常
}catch(NullPointerException e ) {
System.out.println("发现异常:空指针");
}finally{
System.out.println("控制台关闭");
}
}
}
返回结果:
从返回结果看出,程序在捕捉完异常信息之后,会执行finally代码块中的代码,另外,在以下三种特殊情况下,finally块不会被执行,如下:
- 在finally代码块中产生异常;
- 在前面的代码中使用了System.exit()退出程序;
- 程序所在的线程死亡;
3.在方法中抛出异常
如果某个方法可能会产生异常,但不想在当前方法中处理这个异常,则可以使用throws关键字和throw关键字在方法中抛出异常。
3.1throws关键字
throws关键字常被应用于方法上,表示方法可能抛出异常,当方法抛出多个异常时,可用逗号分隔异常类型名,使用throws关键字抛出异常的语法如下:
返回值类型名 方法名(参数表)throws 异常类型名{ }
下面例子中演示throws用法,代码如下:
public class Demo{
public void sum() throws ArithmeticException{ //使用throws关键字抛出ArithmeticException算术异常
int i = 3/0;
System.out.println(i);
}
public static void main(String[] args) {
Demo a = new Demo(); //创建对象
try {
a.sum(); //输出对象方法
}catch(ArithmeticException e) { //对接受的异常做处理
System.out.println("算术发现异常,请检查");
}finally{
System.out.println("控制台关闭");
}
}
}
说明:使用throws关键字将方法产生的异常抛给上一级后,如果上一级不想处理该异常,那么可以继续向上抛出,但最终要有能够捕捉并处理这个异常的代码
3.2throw关键字
throw关键字虽然可以用于抛出Exception类中的子类异常,但更重要的用于是抛出自定义异常使用throw关键字抛出异常的语法如下:
throw new 异常类型名(异常信息)
创建自定义异常,代码如下:
public class Demo{
public static void main(String[] args) {
int b = 0;
if(b == 0) {
//创建自定义异常
throw new ArithmeticException("b不能等于0");
}
}
}
返回结果:
3.3throws关键字和throw关键字的区别
throws关键字和throw关键字的区别如下:
- throws用在方法声明后面,表示抛出异常,由方法的调用者处理,而throw用在方法体内,用来制造一个异常,由方法体内的语句处理;
- throws是声明这个方法会抛出这种类型的异常,以便使它的调用者知道要捕捉这个异常,而throw是直接抛出一个异常实例;
- throws表示出现异常的一种可能性,并不一定会产生这些异常,但如果使用throw,就一定会产生某种异常;
4.异常的使用原则
异常处理的主要作用是捕捉并处理程序在运行时产的异常,编写代码处理某个方法可能出现的异常时,可遵循以下原则:
- 不要过度使用异常,虽然通过异常可以增强程序的健壮性,但使用过多不必要的异常处理,可能会影响程序的执行效率;
- 不要使用过于庞大的try-catch块,在一个try块中放置大量的代码,这种写法看上去“很简单”,但是由于try块中的代码过于庞大,业务过于复杂,会增加try块中出现异常的几率,从而增加分析产生异常原因的难度;
- 避免使用catch(Exception e),如果所有异常都采用相同的处理方式,那么将会导致无法对不同异常进行分类处理;
- 不要忽略捕捉的异常,遇到异常一定要及时处理;
- 如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出新异常;