目录
1.什么是异常
尽管人人希望自己身体健康,处理的事情都能顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。
同样,在程序运行的过程中,也会发生各种非正常状况,例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。
针对这种情况, Java语言引入了异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。
接下来通过一个案例认识一下什么是异常。
1 public class Example24 {
2 public static void main(String[] args) {
3 int result = divide(4, 0); // 调用divide()方法
4 System.out.println(result);
5 }
6 //下面的方法实现了两个整数相除
7 public static int divide(int x, int y) {
8 int result = x / y; // 定义一个变量result记录两个数相除的结果
9 return result; // 将结果返回
10 }
11 }
从运行结果可以看出,程序发生了算术异常(ArithmeticException),该异常是由于文件4-24中的第3行代码调用divide()方法时传入了参数0,运算时出现了被0除的情况。异常发生后,程序会立即结束,无法继续向下执行。
2.异常的分类
上述程序产生的ArithmeticException异常只是Java异常类中的一种,Java提供了大量的异常类,这些类都继承自java.lang.Throwable类。
接下来通过一张图展示Throwable类的继承体系。
Throwable有两个直接子类Error和Exception,其中,Error代表程序中产生的错误,Exception代表程序中产生的异常。
● Error类称为错误类,它表示Java程序运行时产生的系统内部错误或资源耗尽的错误,这类错误比较严重,仅靠修改程序本身是不能恢复执行的。举一个生活中的例子,在盖楼的过程中因偷工减料,导致大楼坍塌,这就相当于一个Error。例如,使用java命令去运行一个不存在的类就会出现Error错误。
● Exception类称为异常类,它表示程序本身可以处理的错误,在Java程序中进行的异常处理,都是针对Exception类及其子类的。在Exception类的众多子类中有一个特殊的子类—RuntimeException类,RuntimeException类及其子类用于表示运行时异常。 Exception类的其他子类都用于表示编译时异常。
2.1 Throwable类中的常用方
方法声明 | 功能描述 |
String getMessage() | 返回异常的消息字符串 |
String toString() | 返回异常的简单信息描述 |
void printStackTrace() | 获取异常类名和异常信息,以及异常出现在程序中的位置, 把信息输出在控制台。
|
2.2 try…catch和finally
出现异常后,程序会立即终止。为了解决异常,Java提供了对异常进行处理的方式一一异常捕获。异常捕获使用try…catch语句实现,try…catch具体语法格式如下:
try{
//程序代码块
}catch(ExceptionType(Exception类及其子类) e){
//对ExceptionType的处理
}
上述语法格式中,在try代码块中编写可能发生异常的Java语句,catch代码块中编写针对异常进行处理的代码。当try代码块中的程序发生了异常,系统会将异常的信息封装成一个异常对象,并将这个对象传递给catch代码块进行处理。catch代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是Exception类或其子类。
接下来通过一个案例演示用try…catch语句对异常进行捕获。
1 public class Example25 {
2 public static void main(String[] args) {
3 //下面的代码定义了一个try…catch语句用于捕获异常
4 try {
5 int result = divide(4, 0); //调用divide()方法
6 System.out.println(result);
7 } catch (Exception e) { //对异常进行处理
8 System.out.println("捕获的异常信息为:" + e.getMessage());
9 }
10 System.out.println("程序继续向下执行...");
11 }
12 //下面的方法实现了两个整数相除
13 public static int divide(int x, int y) {
14 int result = x / y; //定义一个变量result记录两个数相除的结果
15 return result; //将结果返回
16 }
17 }
在程序中,有时候会希望有些语句无论程序是否发生异常都要执行,这时就可以在try…catch语句后,加一个finally代码块。
1 public class Example26 {
2 public static void main(String[] args) {
3 //下面的代码定义了一个try…catch…finally语句用于捕获异常
4 try {
5 int result = divide(4, 0); //调用divide()方法
6 System.out.println(result);
7 } catch (Exception e) { //对捕获到的异常进行处理
8 System.out.println("捕获的异常信息为:" + e.getMessage());
9 return; //用于结束当前语句
10 } finally {
11 System.out.println("进入finally代码块");
12 }
13 System.out.println("程序继续向下执行…");
14 }
15 //下面的方法实现了两个整数相除
16 public static int divide(int x, int y) {
17 int result = x / y; //定义一个变量result记录两个数相除的结果
18 return result; //将结果返回
19 }
20 }
finally中的代码块在一种情况下是不会执行的,那就是在try...catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。
2.3 throws关键字
在实际开发中,大部分情况下我们会调用别人编写方法,并不知道别人编写的方法是否会发生异常。针对这种情况,Java允许在方法的后面使用throws关键字对外声明该方法有可能发生的异常,这样调用者在调用方法时,就明确地知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。
throws关键字声明抛出异常的语法格式如下:
修饰符 返回值类型 方法名(参数1,参数2.....)throws 异常类1, 异常类2.....{
//方法体.....
}
从上述语法格式中可以看出,throws关键字需要写在方法声明的后面,throws后面需要声明方法中发生异常的类型。
接下来通过一个案例演示throws关键字的用法。
1 public class Example27 {
2 public static void main(String[] args) {
3 int result = divide(4, 2); //调用divide()方法
4 System.out.println(result);
5 }
6 //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
7 public static int divide(int x, int y) throws Exception {
8 int result = x / y; //定义一个变量result记录两个数相除的结果
9 return result; //将结果返回
10 }
11 }
在上述代码中,第3行代码调用divide()方法时传入的第二个参数为2,程序在运行时不会发生被0除的异常,但是由于定义divide()方法时声明了抛出异常,调用者在调用divide()方法时就必须进行处理,否则就会发生编译错误。
接下来修改上述程序,在try…catch处理divide()方法抛出的异常。
1 public class Example28 {
2 public static void main(String[] args) {
3 //下面的代码定义了一个try…catch语句用于捕获异常
4 try {
5 int result = divide(4, 2); //调用divide()方法
7 System.out.println(result);
8 } catch (Exception e) { //对捕获到的异常进行处理
9 e.printStackTrace(); //打印捕获的异常信息
10 }
11 }
12 //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
13 public static int divide(int x, int y) throws Exception {
14 int result = x / y; //定义一个变量result记录两个数相除的结果
15 return result; //将结果返回
16 }
17 }
由于使用了try…catch对divide()方法进行了异常处理,因此程序可以编译通过,运行后正确的打印出了运行结果2。下面修改上述程序,将divide()方法抛出的异常继续抛出。
1 public class Example29 {
2 public static void main(String[] args)throws Exception {
3 int result = divide(4, 0); // 调用divide()方法
4 System.out.println(result);
5 }
6 // 下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
7 public static int divide(int x, int y) throws Exception {
8 int result = x / y; // 定义一个变量result记录两个数相除的结果
9 return result; // 将结果返回
10 }
11 }
3.运行时异常与编译时异常
在实际开发中,经常会在程序编译时产生一些异常,这些异常必须要进行处理,这种异常被称为编译时异常,也称为checked异常。另外还有一种异常是在程序运行时产生的,这种异常即使不编写异常处理代码,依然可以通过编译,因此被称为运行时异常,也称为unchecked异常。
4.自定义异常
JDK中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况,例如,前面讲解的程序中的divide()方法,不允许被除数为负数。为了解决这个问题,Java允许用户自定义异常,但自定义的异常类必须继承自Exception或其子类。
自定义异常的具体代码如下所示。
// 自定义异常类
class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}
// 在程序中使用自定义异常类
public class Main {
public static void main(String[] args) {
try {
throw new CustomException("这是自定义的异常信息");
} catch (CustomException e) {
System.out.println("捕获到自定义异常:" + e.getMessage());
}
}
}
5.编程尝试
编写一个程序,要求用户输入两个整数,并计算它们的商。在计算过程中,需要进行异常处理以防止除数为 0 的情况。如果除数为 0,则抛出自定义的异常 DivideByZeroException,并在捕获异常后输出提示信息。
要求:
6.如何在控制台和用户交互输入信息?
如何在控制台和用户交互输入信息?
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = scanner.nextInt();
System.out.println("您输入的整数是:" + num);
scanner.close();
}
}