异常概述
一、 认识异常
异常是指在程序运行过程中发生的不正常事件,比如所需文件找不到、网络连接不通或网络中断、数组下标越界、类型转换异常等。异常会中断正在运行的程序。
可以通过下面的代码认识异常:
import java.util.Scanner;
/**
* 演示程序中的异常。
* @author administrator
*/
public class Test1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("请输入被除数:");
int num1 = in.nextInt();
System.out.print("请输入除数:");
int num2 = in.nextInt();
System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
System.out.println("感谢使用本程序!");
}
}
这段代码中,此处将 num1 和 num2 都以整型的形式输出。
正常情况下,用户会按照系统的提示输入整数,除数不能为0,但是如果用户没有按照要求进行输入,如除数输入了非整型,则程序运行时将会发生异常,输出结果如图所示:
如果输入0,则程序运行时也会发生异常,输出结果如图所示:
从运行结果可以看出,一旦出现异常程序将会立刻结束,计算和输出的语句则不会执行,也可以通过if-else语句对各种异常情况进行判断处理,代码如下所示:
import java.util.Scanner;
/**
* 传统处理程序中的异常。
* @author administrator
*/
public class Test2 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("请输入被除数:");
int num1 = in.nextInt();
System.out.print("请输入除数:");
int num2 = 0;
if (in.hasNextInt()) { // 如果输入的除数是整数
num2 = in.nextInt();
if (0 == num2) { // 如果输入的除数是0
System.err.println("输入的除数是0,程序退出。");
System.exit(1);
}
System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
System.out.println("感谢使用本程序!");
} else { // 如果输入的除数不是整数
System.err.println("输入的除数不是整数,程序退出。");
System.exit(1);
}
}
}
但是,通过if-else进行异常处理,有以下几个缺点:
- 代码臃肿,加入了大量的异常情况判断和处理代码。
- 容易分散过多的精力处理异常,影响开发效率。
- 很难举例所有的异常情况。
- 异常处理代码和业务代码交织在一起,影响代码可读性,容易加大日后的程序的维护难度。
二、Java异常体系结构
Java中的异常有很多种类型,异常体系结构如图:
所有异常类型都是Throwable类的子类,它派生了两个子类:Error类和Exception类。
三、Java异常处理
Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws。
(1)try:执行可能产生异常的代码
(2)catch:用来捕获异常
(3)finally:无论是否发生异常,代码总能执行
(4)throw:手动抛出异常,可写在try、catch、 finally中以及代码块之外
(5)throws:声明方法可能要抛出的各种异常 ,放在参数旁边
- 使用try-catch处理异常
使用try-catch块捕获异常,分为三种情况:
(1)第一种情况 :正常
public void method(){
try {
// 代码段(此处不会产生异常)
} catch (异常类型 ex) {
// 对异常进行处理的代码段
}
// 代码段
}
(2)第二种情况:出现异常
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
// 代码段 3
} catch (异常类型 ex) {
// 对异常进行处理的代码段4
}
// 代码段5
}
异常是一种特殊的对象,类型为java.lang.Exception或其子类。
如果try语句块在执行过程中发现异常,并且这个异常与catch语句块中声明的异常类型匹配,那么try中剩下的代码都将被忽略,而catch中的语句继续执行。
当在被除数输入了A,则抛出InputMismatchException异常,由InputMismatchException是Exception的子类,程序将忽略try语句块中剩下的代码而执行catch语句块。输出结果如图所示:
如果输入除数为0,输出结果如下图所示:
(3)第三种情况:异常类型不匹配
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
// 代码段 3
} catch (异常类型 ex) {
// 对异常进行处理的代码段4
}
// 代码段5
}
如果try语句块在执行过程中发生异常,而抛出的异常在catch语句块中没有被声明,那么方法立刻退出。
在catch语句块中可以加入用户自定义处理信息,也可以调用异常对象的方法输出异常信息,常用方法如下:
(1)void getMessage():返回异常的堆栈信息。堆栈信息包括程序运行到当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用序列。
()String getMessage():返回异常信息描述字符串,该字符串描述了异常产生的原因,是printStacktrace()输出信息的一部分。
注意:如果try语句块在执行过程中发生异常,try语句块中剩下的代码都将被忽略,系统会自动生成相应 的异常对象,包括异常的类型、异常出现时程序的运行状态及对该异常的详细描述。如果这个异常对象与catch语句块中声明的异常匹配类型匹配,会把该异常对象赋给catch后面的异常参数,相应的catch语句将会被执行。
-
在catch块中处理异常:加入用户自定义处理信息
System.err.println("出现错误:被除数和除数必须是整数 "); -
调用方法输出异常信息:e.printStackTrace();
-
异常对象常用的方法:
-
常见的异常类型
- try-catch-finally处理异常
如下面代码所示:
import java.util.InputMismatchException;
import java.util.Scanner;
public class Text7 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("请输入被除数:");
try{
int num1 = in.nextInt();
System.out.print("请输入除数:");
int num2 = in.nextInt();
if(num1<num2){
return;
}
System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
}catch (InputMismatchException e){
System.err.println("被除数和除数必须为整数");
}catch (ArithmeticException e){
System.err.println("除数不能为0");
}catch (Exception e){
System.err.println("其他未知异常");
}finally {
System.out.println("程序继续");
System.out.println("程序结束");
}
}
}
(1) 在try-catch块后加入finally块
特点:是否发生异常都执行;不执行的唯一情况,如下图所示:
(2)存在return的try-catch-finally块
如下面代码所示:
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
} catch (异常类型 ex) {
// 对异常进行处理的代码段3
return;
}finally{
// 代码段 4
}
}
try-catch- finally语句块的执行流程大致分为如下两种情况:
- 如果try语句块中所有语句正常执行完毕,finally 语句块也会被执行。例如,当在控 制台输入两个数字时,示例4中的try语句块中的代码将正常执行,不会执行catch语句块中的代码,但是finally语句块中的代码将被执行。
- 如果try语句块在执行过程中发生异常,无论这种异常能否被catch语句块捕获到,都将执行finally 语句块中的代码。例如,当在控制台输入的除数为0时,示例4中的try语句块中将抛出异常,进入catch语句块,最后finally 语句块中的代码也将被执行。
注意:即使在catch语句块中存在return语句,finally语句块中的语句也会执行。发生异常时的执行顺序是,先执行catch语句块中return之前的语句,再执行finally语句块中的语句,最后执行catch语句块中的return语句退出。
-
多重catch块
1> 排列catch 语句的顺序:先子类后父类
2> 发生异常时按顺序逐个匹配
3> 只执行第一个与异常类型匹配的catch语句在计算并输出商的示例中,至少存在两种异常情况,输入非整数内容和除数为0,可使用多重catch 语句块分别捕获并处理对应异常。
一段代码可能会引发多种类型的异常,这时,可以在一个try语句块后面跟多个catch语句块分别处理不同的异常。但排列顺序必须是从子类到父类,最后一个一般都是Exception类。因为按照匹配原则,如果把父类异常放到前面,后面的catch语句块将不会被执行机会。
运行时,系统从上到下分别对每个catch语句块处理的异常类型进行检测,并执行第一个与异常类型匹配的catch语句。执行其中的一条catch语句之后, 其后的catch可将被忽略。 -
使用throws声明抛出异常
(1)如果在一个方法体中抛出了异常,并希望调用者能够及时地捕获异常,Java 语言中通过关键字throws声明某个方法可能抛出的各种异常以通知调用者。throws可以同时声明多个异常,之间由逗号隔开。
(2) 如下面代码所示,把计算并输出商的任务封装在了divide() 方法中,并在方法的参数列表后通过throws声明抛了异常,然后在main()方法中调用该方法,此时main()方法就知道divide()方法中抛出了异常,可以采用如下两种方式进行处理。
1> 通过try-catch捕获并处理异常。
2>通过throws继续声明变量,如果调用者不知道如何处理该异常,可以继续通过throws声明异常,让上一级调用者处理异常。main()方法声明的异常将由Java虚拟机来处理。
import java.util.InputMismatchException;
import java.util.Scanner;
public class Text2 {
public static void main(String[] args) {
try{
divide();
}catch (InputMismatchException e){
System.err.println("被除数和除数必须为整数");
}catch (ArithmeticException e){
System.err.println("除数不能为0");
}catch (Exception e){
System.err.println("其他未知异常");
}finally {
System.out.println("程序继续");
System.out.println("程序结束");
}
}
public static void divide() throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("请输入被除数:");
int num1 = in.nextInt();
System.out.print("请输入除数:");
int num2 = in.nextInt();
System.out.println(num1+"/"+ num2 +"="+ num1/ num2);
}
}
- 使用throw抛出异常
除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现并解决的,如年龄不在范围内,性别输入不是“男”或“女”,所以需要在 方法内部通过throw抛出异常,如下面代码所示:
public class Person {
private String name = "";// 姓名
private int age = 0;// 年龄
private String sex = "男";// 性别
/**
* 设置性别。
* @param sex 性别
* @throws Exception
*/
public void setSex(String sex) throws Exception {
if ("男".equals(sex) || "女".equals(sex))
this.sex = sex;
else {
throw new Exception("性别必须是“男”或者“女”!");
}
}
/**
* 打印基本信息。
*/
public void print() {
System.out.println(this.name + "(" + this.sex
+ "," + this.age + "岁)");
}
}
//输出基本信息
public class Test8 {
public static void main(String[] args) {
Person person = new Person();
try {
person.setSex("Male");
person.print();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果如下所示:
*注意:
throw 和 throws的区别:
- 作用不同:throw用于程序员自行产生并抛出异常,throws用于声明该方法内抛出了异常。
- 使用位置不同:throw用于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表的后面,不能单独使用。
- 内容不同:throw抛出一个异常对象,只能是一个;throws后面跟异常类,可以跟多个。*