1. 异常基本概念
-
在没有异常处理的语言中如果要回避异常,就必须使用判断语句,配合所想到的错误状况来捕捉程序里所有可能发生的错误。但是为了捕捉这些错误,编写出来的程序代码通常会有大量的判断语句,有时候这样也未必能捕捉到所有的错误,而且这样做势必会导致程序运行效率的降低。
Java 的异常处理机制恰好改进了这一点。它具有易于使用、可自行定义异常类,处理抛出异常的同时又不会降低程序运行的速度等优点。 因而在 Java 程序设计时,应充分利用 Java 的异常处理机制,以增加程序的稳定性及效率。 -
如果在 try 中产生了异常,则程序会自动跳转到 catch 语句中找到匹配的异常处理类型进行相应的处理。最后不管程序是否会产生异常,则肯定会执行到 finally 语句,finally 语句作为异常的统一出口。finall 块是可以省略的,如果省略了 finally 块,则在 catch 块运行结束之后,程序跳转到 try-catch 块之后继续执行。
-
try中抛出异常,有匹配的catch语句,则catch语句捕获,如果catch中有return语句,则要在finally执行后再执行return;
-
finally中有return语句,当try执行到return时会执行finally中的代码,其中有return 就直接返回了
-
finally中不含return语句,那么当执行到return时,它会被保存等待finally执行完毕后返回,这个时候无论finally内部如何改变这个值,都不会影响返回结果。
-
在try语句块或catch语句块中执行到System.exit(0)直接退出程序。
2. 在程序中使用异常处理
实例 1 代码:
package self.learn.exception;
public class Exception01 {
public static void main(String[] args) {
System.out.println("****** 计算开始 ******");
int i = 0;
int j = 0;
try {
String str1 = args[0]; // 结收第一个参数
String str2 = args[1]; // 结收第二个参数
i = Integer.parseInt(str1); // 将第 1 个参数由字符串变为整型
j = Integer.parseInt(str2); // 将第 2 个参数由字符串变为整型
int temp = i/j; // 此处会产生异常
System.out.println("两个数字相除结果:"+temp); // 此代码步再执行
System.out.println("-------------"); // 此代码步再执行
} catch (ArithmeticException e) { // 捕捉算术异常
System.out.println("出现异常了:"+e);
System.out.println("算术异常:"+e.getMessage());
}
System.out.println("计算结束");
}
}
运行结果截图:
上面的程序其实存在上比较明显的异常:
(1)输出找出绑定异常:ArrayIndexOutOfBoundsException (不传参数就会出现)。
(2)数字格式化异常:NumberFormatException(传两个字符参数 a,b 就会出现异常)。
(3)算术异常:ArithmeticException (传 1 和 0 就会出现异常,分母为 0)。
要想保持程序的执行正确,就必须对 3 个异常进行处理,所以此时的 catch 语句应该有 3 个,以区分处理不同的异常。
package self.learn.exception;
public class Exception01 {
public static void main(String[] args) {
System.out.println("****** 计算开始 ******");
int i = 0;
int j = 0;
try {
String str1 = args[0]; // 结收第一个参数
String str2 = args[1]; // 结收第二个参数
i = Integer.parseInt(str1); // 将第 1 个参数由字符串变为整型
j = Integer.parseInt(str2); // 将第 2 个参数由字符串变为整型
int temp = i/j; // 此处会产生异常
System.out.println("两个数字相除结果:"+temp); // 此代码步再执行
System.out.println("-------------"); // 此代码步再执行
} catch (ArithmeticException e) { // 捕捉算术异常
System.out.println("出现异常了:"+e);
System.out.println("算术异常:"+e.getMessage());
}catch (NumberFormatException e) {
System.out.println("数字转换异常:"+e); // 处理数字转换异常
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常:"+e); // 处理数组越界异
}
System.out.println("计算结束");
}
}
运行结果截图:
上面的程序使用了 3 个 catch 语句分别处理 3 个不同的异常,但是每个程序的异常都使用这种方式处理的话肯定会很麻烦,因为在程序开发过程中很难知道到底会有多少个异常。要想解决这个问题,首先要从异常的整体结构来分析。
提示: Java 的异常处理格式也在不断改变
在正常的异常处理中,异常处理的语句格式有 try…catch 和 try…catch…finally。但是随着 Java 的发展,对应异常的处理语句也可以采用 try…finally 的形式编写。可以不写 catch 语法,但是这样的操作没有多大意义。
3 异常类的处理结构
在整个 Java 的异常结构中,实际上有两个常用的类:Exception、Error,这两个都是 Throwable 的子类,如图:
- Exception: 一般表示的是程序中出现的问题,可以直接使用 try…catch 处理。
- Error: 一般指的是 JVM 错误,程序中无法处理。
一般情况下,比较习惯把以上的两者统称为异常。
异常信息的输出方式:
- 可以直接使用 System.out.println(异常对象);
- 使用 Exception 类中的 printStackTrace() 方法输出异常信息,如:e.printStackTrace() 。这种方式打印的异常信息是最全的。
4 Java 的异常处理机制
- 在整个 Java 的异常处理中,实际上也是按照面向对象的方式进行处理的,处理步骤如下:
(1)一旦产生异常,则首先会产生一个异常类的实例化对象;
(2)在 try 语句中对此异常对象进行捕捉;
(3)产生的异常对象与 catch 语句中的各个异常了进行匹配,如果匹配成功,则执行 catch 语句中的代码。 - 根据对象的多态性,所有的子类实例可以全部使用父类接收,那么就可以用向上转型的概念,让所有的异常对象都使用 Exception 接收。
4.1 使用 Exception 处理其他异常
package self.learn.exception;
public class Exception01 {
public static void main(String[] args) {
System.out.println("****** 计算开始 ******");
int i = 0;
int j = 0;
try {
String str1 = args[0]; // 结收第一个参数
String str2 = args[1]; // 结收第二个参数
i = Integer.parseInt(str1); // 将第 1 个参数由字符串变为整型
j = Integer.parseInt(str2); // 将第 2 个参数由字符串变为整型
int temp = i/j; // 此处会产生异常
System.out.println("两个数字相除结果:"+temp); // 此代码步再执行
System.out.println("-------------"); // 此代码步再执行
} catch (ArithmeticException e) { // 捕捉算术异常
System.out.println("出现异常了:"+e);
System.out.println("算术异常:"+e.getMessage());
}catch (NumberFormatException e) {
System.out.println("数字转换异常:"+e); // 处理数字转换异常
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常:"+e); // 处理数组越界异
}catch (Exception e) {
System.out.println("其他异常:"+e); // 其他异常处理
}
System.out.println("计算结束");
}
}
注意:在 Java 中所有捕获范围小的异常必须放在捕获大的异常前面,否则程序在编译的时候就会出现错误提示。如图:
因为 Exception 捕捉的范围最大,所以以后的全部异常都是不可处理的,根据这样一个概念,一般在开发时,不管出现任何异常,都可以直接使用 Exception 进行处理,这样会比较方便。
package self.learn.exception;
public class Exception01 {
public static void main(String[] args) {
System.out.println("****** 计算开始 ******");
int i = 0;
int j = 0;
try {
String str1 = args[0]; // 结收第一个参数
String str2 = args[1]; // 结收第二个参数
i = Integer.parseInt(str1); // 将第 1 个参数由字符串变为整型
j = Integer.parseInt(str2); // 将第 2 个参数由字符串变为整型
int temp = i/j; // 此处会产生异常
System.out.println("两个数字相除结果:"+temp); // 此代码步再执行
System.out.println("-------------"); // 此代码步再执行
}catch (Exception e) {
System.out.println("两个数字相除出现异常:"+e);
}
System.out.println("计算结束");
}
}
提问:可不可以直接使用 Throwable ?
回答:不建议这样使用,最大只能捕获 Exception
首先使用 Throwable 捕获异常,这在代码中是没有任何的问题,因为 Throwable 捕获的范围是最大的。但一般开发找那个是不会直接使用 Throwable 进行捕获的,对于 Throwable 来说有 Exciption、Error 两个子类,Error 类本身不需要程序处理,而程序中需要处理的只是 Exception,所以没有必要使用 Throwable。
另外,对于一个程序,如果有多个异常最后分别进行捕获,而不是直接使用 Exception 捕获全部。