12.异常
本章目标
- 异常简介
- 异常处理
本章内容
基于IEmployeeDao对应的实现类中,会有哪些常见出现?比如修改可能会出现空指针异常
一、异常简介
1、什么是异常
异常是应用程序在运行过程中出现的错误或非正常的意外情况,即虚拟机的通常操作中可能遇到的异常,是一种常见的运行错误。
常见情况:数组下标越界、空指针的访问、试图读取不存在的文件、数学除零等
完全不出错是不可能的,不要刻意回避,要多想意外发生后如何去处理它!
2、为什么用异常
运行时发生的错误如果没有异常处理机制,程序将会终止并使所有已分配资源的状态保持不变,这样会导致资源泄露。
异常处理机制由编译器强制执行
如:不能收回某个程序分配的内存,以供其它程序使用。
3、异常的分类
Java中每种异常都是使用一个Java类来表示
在Java语言中,异常
可以看作是一个类,异常类的根是Throwable。Throwable是类库java.lang包中的一个类,并派生出Exception类和Error类两个子类
3.1、异常类的结构
Throwable
|____Error
|____Exception
|____RuntimeException
|____其它Exception
Error:Java运行系统中的内部错误以及资源耗尽的情况。
Exception:RuntimeException及其子类(编程导致的异常)、其它Exception类
3.2、Error体系
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。
应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。
如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。
所以,在进行程序设计时,应该更关注Exception体系。
3.3、Exception体系
Exception
体系包括RuntimeException
体系和其他非RuntimeException
的体系 :
-
RuntimeException
:该体系包括错误的类型转换、数组越界访问和试图访问空指针等等。如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
-
其他非
RuntimeException
(编译期异常):这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误
3.4、异常类的层次结构
二、异常处理
1、异常产生过程
异常的产生,大致经历以下几个阶段:
- Java程序在执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
- 当Java运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
- 如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出
所以需要对异常进行处理,否则程序将立即终止,无法继续执行。
2、异常处理方式
对于异常有两种处理方法 :
- 捕获异常:通过try … catch方法,catch子句中放置处理异常的语句。
- 向上抛异常:在方法名后面加上throws Exception…, 方法本身只是抛出异常,由函数调用者来捕获异常。
3、try…catch
3.1、基本结构:
try{
监控代码
}catch(异常类型){
处理语句
}
- catch后面小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
- catch可以写多个,从上到下,必须遵守从小到大。
- 建议catch的时候,精确的一个一个处理,有利于程序的调试
3.2、示例
在控制台输入员工年龄,如果输入的是非数字类型,会抛异常InputMismatchException
类型不匹配
注:为了测试异常方便,我们直接在addEmployee,来接收数据,实际业务中addEmployee方法只用来接收传过来的对象,而不是在方法里接收这些数据。
public class EmployeeDaoImpl {
public void addEmployee(Employee employee) {
Scanner scanner = new Scanner(System.in);
……
System.out.println("请输入员工年龄");
try {
int age = scanner.nextInt();
employee.setAge(age);
} catch (InputMismatchException e) {
System.out.println("类别不匹配");
}
scanner.close();
}
}
3.3、嵌套try-catch块
有时块的一部分导致一个错误,而块本身也可能导致一个错误,必须将一个异常嵌套在另一个异常处理机制中
从内层try语句寻找与异常匹配的catch语句,如无,检查下一个,从里到外,直到最后一个catch
在操作的同时,有可能还会出现空打针异常,
public class EmployeeDaoImpl {
public void addEmployee(Employee employee) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工编号");
int empId = scanner.nextInt();
employee.setEmpId(empId);
System.out.println("请输入员工姓名");
String empName = scanner.next();
employee.setEmpName(empName);
System.out.println("请输入员工年龄");
try {
int age = scanner.nextInt();
try {
employee.setAge(age);
} catch (NullPointerException ee) {
System.out.println("空指针了");
}
} catch (InputMismatchException e) {
System.out.println("类别不匹配");
}
scanner.close();
}
}
3.4、多重 catch 块
一段代码可能会生成多个异常
当引发异常时,会按顺序来查看每个 catch 语句,并执行第一个类型与异常类型匹配的语句
执行其中的一条 catch 语句之后,其他的 catch 语句将被忽略
使用多重 catch 语句时,异常子类一定要位于异常父类之前 ,否则,控制权将永远传不到子类,将产生一个不能到达的代码,即产生错误
public class EmployeeDaoImpl {
public void addEmployee(Employee employee) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工编号");
int empId = scanner.nextInt();
employee.setEmpId(empId);
System.out.println("请输入员工姓名");
String empName = scanner.next();
employee.setEmpName(empName);
System.out.println("请输入员工年龄");
try {
int age = scanner.nextInt();
employee.setAge(age);
} catch (InputMismatchException e) {
System.out.println("类别不匹配");
} catch (NullPointerException ee) {
System.out.println("空指针了");
}
scanner.close();
}
}
3.5、java8新特性
可以这样书写:
catch(FileNotFoundException | ArithmeticException | NullpointerException e) { … }
示例
public class EmployeeDaoImpl {
public void addEmployee(Employee employee) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工编号");
int empId = scanner.nextInt();
employee.setEmpId(empId);
System.out.println("请输入员工姓名");
String empName = scanner.next();
employee.setEmpName(empName);
System.out.println("请输入员工年龄");
try {
int age = scanner.nextInt();
employee.setAge(age);
} catch (InputMismatchException |NullPointerException e) {
System.out.println("出现异常");
}
scanner.close();
}
}
4、finally 块
finally语句的主要作用是在try或catch转到其他部分前做的一些善后工作。比如:关闭打开的文件,释放链接、内存等系统资源。
有时需要清理代码,但程序已经停止运行,使用finally 块可使程序不管发生什么异常都将执行
4.1、finally 块作用
- finally是异常处理语句结构的一部分,表示无论什么情况都要执行的模块。
- finally语句的执行是一种无条件的操作,无论在哪种异常下都会执行,即使try或catch模块中有continue、return、break等关键字,或者是有throw语句,程序都会执行finally 语句。
- 每个try语句至少有一个catch子句或finally子句
4.2、try、catch 和 finally 块的执行流程
4.3、示例
完整结构为try catch finally
,也可以写成try catch
,或try finally
public void addEmployee(Employee employee) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工编号");
int empId = scanner.nextInt();
employee.setEmpId(empId);
System.out.println("请输入员工姓名");
String empName = scanner.next();
employee.setEmpName(empName);
System.out.println("请输入员工年龄");
try {
int age = scanner.nextInt();
employee.setAge(age);
} catch (InputMismatchException |NullPointerException e) {
System.out.println("出现异常");
}finally {
System.out.println("无论如何都会执行,释放内存等善后工作");
}
scanner.close();
}
5、throws关键字
throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常。对大多数Exception子类来说,Java 编译器会强迫你声明在一个成员函数中抛出的异常的类型
抛非运行时异常,运行时异常一般是程序员的问题
5.1、 向上抛
public class EmployeeDaoImpl {
public void queryById() throws ClassNotFoundException {
Class.forName("");
}
}
5.2、测试类中处理
public static void main(String[] args) {
EmployeeDaoImpl dao = new EmployeeDaoImpl();
try {
dao.queryById();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
6、throw语句
throw
关键字通常用在方法体中,并且抛出一个异常对象。
- 程序在执行到
throw
语句时立即停止,它后面的语句都不执行。 - 通过
throw
抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws
关键字在方法声明中指明要抛出的异常; - 如果要捕捉
throw
抛出的异常,则必须使用try—catch
语句
本例子接合自定义异常展示
7、自定义异常
JavaAPI提供的内置异常不一定总能捕获程序中发生的所有错误。有时会需要创建用户自定义异常
7.1、格式
public class XXXExcepiton extends Exception | RuntimeException{
提供无参数的构造方法
或
提供一个有参数的构造方法,可自动生成
}
注意: 1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类 2.自定义异常类,必须的继承Exception或者RuntimeException 3.继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch 4.继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
7.2、自定义异常MyException
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
public MyException() {
}
public MyException(String message) {
super(message);// 把参数传递给Throwable的带String参数的构造方法
}
}
7.3、案例 (贯穿项目相关)
如果用户输入的年龄小于18,则抛出自定义异常
public static void main(String[] args) throws MyException {
……
else if(opertor == 2) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工编号");
int empId = scanner.nextInt();
employee.setEmpId(empId);
System.out.println("请输入员工姓名");
String empName = scanner.next();
employee.setEmpName(empName);
System.out.println("请输入员工年龄");
int age = scanner.nextInt();
//年龄小于18抛出异常对象
if(age<18) {
throw new MyException("用户年龄不能小于18");
}
employee.setAge(age);
scanner.close();
}
}
注意,在方法上面也需要通过throws抛异常
7.4、测试类中调用
public class TestException {
public static void main(String[] args) {
Employee employee = new Employee();
EmployeeDaoImpl dao = new EmployeeDaoImpl();
try {
dao.addEmployee(employee);
} catch (MyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(employee);
}
}
8、throw 和 throws总结
- throws说明你有哪个可能,倾向
- throw的话,那就是你把那个倾向变成真实的了同时:
- throws出现在方法函数头;而throw出现在函数体;
- throws表示出现异常的一种可能性,并不一定会发生这些异常;
- throw则是抛出了异常,执行throw则一定抛出了某种异常;
- 两者都是消极处理异常的方式(这里的消极并不是说这种)
思维导图
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18802596