概述
- 异常就是程序运行过程中可能产生的问题。
异常继承体系
异常的分类
- 严重性问题:用错误Error类表示(一般不解决也没有能力解决)
- 非严重性问题:(用异常类Exception表示)
①. 运行期异常:发生在程序运行过程中产生的问题,只要是RunTimeException或者RunTimeException的子类都是运行时产生的异常。
②. 编译期异常:发生在程序编译时期产生的问题。除了RunTimeException都是编译期的异常,就是Exception的子类。
将以上类的共性向上抽取即有了Throwable父类。
编译期异常和运行期异常的区别
- 运行期异常如果没有处理,程序是==可以执行==。
- 编译期异常如果没有处理,程序是==不能执行==的。
JVM会对异常的处理
- 当产生异常后,JVM在进行处理的时候,首先会==停止程序==,直接结束JVM,因此有异常的语句的后面的代码都不会执行。
将错误信息打印到控制台。
- 由于JVM会停止整个程序,有时我们可能会需要让程序继续往下执行,显然JVM的操作不太符合需求。因此我们有时需要人为自定义异常的处理,这样一来异常处理后,代码就能继续往下执行。
Throwable的几个常见方法
方法名 | 作用 |
---|---|
String getMessage ( ) | 获取异常信息 |
String toString ( ) | 获取异常类名和异常信息 |
void printStackTrace ( ) | 获取异常类名和异常信息,以及异常出现在程序中的位置 |
- Exception作为Throwable的子类,也继承了同样的方法
异常处理的方式
- 使用try…catch…finally语句 处理异常
//try...catch完整格式
try{
//有可能发生异常的语句
}catch(异常类型 变量名){
//异常处理方式
}finally{
//始终要执行的代码,可以做一些收尾的工作,可省略
}
- 注意:
- try中的代码越少越好
- catch中要做处理,哪怕是一条输出语句也可以。( 不能将异常信息隐藏 )
1.第一种格式:针对单个异常的处理方式
try{
//有可能发生异常的语句
}catch(异常类型 变量名){
//异常处理方式
}
public class ExceptionDemo {
public static void main(String[] args) {
// 使用try...catch语句对异常进行一个处理
int a = 4;
int b = 0;
try{
// 可能产生问题的代码
System.out.println(a/b);
System.out.println("hello");
}catch(ArithmeticException e) {
// 对异常的处理方式
System.out.println("我捕获到了异常了。。。。");
}
System.out.println("world");
}
}
2. 第二种格式: 针对多个异常的处理方式。
try{
//可能产生问题的代码
}catch(异常类型1 变量名) {
//对异常类型1的处理
}catch(异常类型2 变量名) {
//对异常类型2的处理
}catch(异常类型3 变量名) {
//对异常类型3的处理
}....
- 注意事项:
- 能明确的尽量明确到具体异常,不要轻易用相对当前异常较上级的父类异常来处理。
- 平级关系的异常谁前谁后无所谓,如果出现了==父子关系==,==父异常必须在后面。==
//针对多个异常的处理方式
public class ExceptionDemo {
public static void main(String[] args) {
int a = 4;
int b = 1;
int[] arr = {1,2,3,4,5};
try {
System.out.println(a/b);
System.out.println(arr[2]);
arr = null;
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组角标越界啦....");
}
catch (ArithmeticException e) {
System.out.println("除数为0了....");
} catch (Exception e) {
System.out.println("产生了异常了.....");
}
System.out.println("over");
}
}
3. JDK1.7的新特性:对多个异常的处理的简化形式
格式:
try{
//可能产生异常的代码
}catch(异常类型1 | 异常类型2 | 异常类型3 | ... 变量名) {
}
注意事项:
1.处理方式是一致的。(实际开发中,好多时候可能就是针对同类型的问题,给出同一个处理)
2.多个异常间必须是平级关系。
//对多个异常的处理的简化形式
public class ExceptionDemo {
public static void main(String[] args) {
int a = 5;
int b = 0;
int[] arr = {};
try{
// System.out.println(a/b);
System.out.println(arr[9]);
arr = null;
System.out.println(arr[2]);
}catch(ArithmeticException | ArrayIndexOutOfBoundsException | NullPointerException e){
System.out.println("产出异常了。。。。");
}
System.out.println("over");
}
}
throws的方式处理异常 (理解)
- 定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。
- throws表示的意思是:在该方法上声明出一个异常,表示该方法在调用的时候有可能产生该种异常。
语法:
public void add() throws 异常类型1,异常类型2..{
}
- 如果上级或者所有的上级调用都没有处理该异常,异常会层级往上抛直到JVM处理异常,即停止整个程序。
throw的概述以及和throws的区别
- throws:关键字,用在方法的声明上,用来表示该方法被调用的时候有可能会产生某些异常。格式:
throws 异常类型1 , 异常类型2 ,... {}
- throw关键字,用来在程序内部(方法中)抛出一个异常。表示的意思是实实在在的产生了一个异常,并抛出去。格式:
throw 异常对象;
public class ExceptionDemo {
public static void main(String[] args) {
//try {
// show(4, 0);
//} catch (Exception e) {
// System.out.println("产生异常了。。。。");
//}
try {
show(4,0);
} catch (Exception e) {
System.out.println("产生异常了。。。。");
}
System.out.println("over....");
}
public static void show(int a ,int b) throws ArithmeticException{
if(b == 0) {
// 如果b==0,那么执行a/b肯定会抛出一个异常
// 手动的向外面抛出一个异常。
// try {
// throw new ArithmeticException("除数为0了");
// } catch (Exception e) {
// System.out.println("产生异常了。。。。");
// }
throw new ArithmeticException("除数为0了");
} else {
System.out.println(a/b);
}
}
}
finally关键字
特点
- 被finally控制的语句体不管有没有异常抛出最终都会执行
- 特殊情况:在执行到finally之前jvm退出了(比如System.exit(0)),则finally才不会执行。
作用
- 用于释放资源,在IO流操作和数据库操作中会见到
有关finally的面试题
final, finally和finalize的区别?
- 答:
- final是一个修饰符,修饰类,方法,变量。
- 修饰的类,不能被继承**
- 修饰的方法,不能被子类重写
- 修饰的变量,本质上是一个常量。
- final如果修饰的是引用数据类型,指的对象的地址值不能发生改变;如果修饰的是基本数据类型,指的是基本数据类型的值不能发生改变
- finally:是一个关键字,用在try…catch语句中
- 特点:始终都会被执行
- 作用:做一些收尾的工作,比如释放资源,释放数据库链接
- finalize:是Object类中的一个方法,跟垃圾回收有关。当对象变成垃圾的时候,如果垃圾回收器正在回收这个对象,那么会调用该对象的finalize(); 相当于C++中的析构函数的作用。
如果catch里面有return语句,请问finally的代码还会执行吗? 如果会,请问是在return前还是return后。
- 答:会,在return之前执行。
try…catch…finally的多种格式变形
- try { } catch () { }
- try { } catch ( ) { } finally { }
- try { } finally { }
自定义异常
自定义异常必须做到以下两点之一:
- 继承自Exception
- 继承自RuntimeException】
/**
* 分数不合法的异常类
*
*/
public class ScoreInvalidateException extends RuntimeException{
public ScoreInvalidateException() {
super();
}
public ScoreInvalidateException(String message) {
super(message);
}
自定义异常小练习
- 需求:从银行取钱,发现钱不够,给出提示。
- 查看参考答案
注意事项 ( 针对编译期异常 ) ( 了解 )
- 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类,子类的方法也可以不抛出异常。(父亲坏了,儿子不能比父亲更坏)
- 如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
- 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws(多线程会出现这个情况)
如何使用异常处理(了解)
- 原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
- 区别:
- 后续程序需要继续运行就try
- 后续程序不需要继续运行就throws