目录
1.异常
Java程序运行过程中出现不正常的情况,导致程序运行中断。
2.异常处理
在程序中设置好异常的处理方式,一旦遇到异常,根据异常类型对异常进行相应的处理,处理完毕后程序继续运行。
- 如果我们没有对程序进行异常处理,JVM会对异常进行默认处理,将异常的信息、原因等输出并结束程序。
- 异常处理机制为程序提供了错误处理的能力。
3.Java的异常体系结构
Java对程序中的各种错误信息封装。
- Throwable:异常类的根类,它有两个子类:Error和Exception
- Error:程序无法处理的错误,大多是程序员无法解决的错误,比如内存溢出等。
- Exception:又分为运行期异常(RuntimeException及其子类)编译期异常(其他Exception)
- 运行期异常:通常是代码不严谨造成的,可以处理也可以不处理
- 编译期异常:必须对异常进行处理,否则编译不通过
4.处理异常的两种方式
4.1 try...catch...finally捕获异常
try{
可能出现异常的代码;
}catch(异常类型 异常变量名){
针对异常的处理;
}finally{
无论有无异常都要执行的代码(比如释放资源);
}
- try:可能出现异常的代码,这部分代码不能太多,否则太消耗资源
- catch:
- 声明的异常类型尽量明确,无法明确的用Exception捕获(效率会降低)
- 必须要有处理代码,否则只是隐藏了异常并没有对异常进行处理
- 可以由多个catch,此时必须父异常在后,否则子类异常无意义
- 一旦try中出现异常,JVM就会抛出一个异常对象与catch中的异常类型进行匹配,匹配上就执行该catch中的处理代码,然后结束try...catch语句(后面的catch都不匹配了),继续执行后面的代码。
- finally:finally中的语句一定会执行。
JDK1.7中捕获多个异常的新方案
try{
可能出现异常的代码;
}catch(异常类型1 | 异常类型2 | ... 异常变量名){
针对上述异常的处理;
}
局限:
- 多个异常只能是平级关系;
- 对这多个异常的处理是同一种方式(实际开发中可行)
4.2 throws抛出异常
针对不想处理或没有能力处理异常的情况,Java提供了异常抛出的解决方案。
在方法上通过关键字throws声明(参数列表后)可能出现的异常类型(多个异常类型之间用逗号隔开),并没有真正的处理异常,可以理解为通知了方法调用者此处可能出现这个异常,并将异常抛给调用者进行处理。
注意:
- 尽量不要将异常抛给main方法,此时由JVM默认处理异常方式处理:输出异常相关信息并中断程序,异常之后的代码不会执行
//运行期的异常抛出处理
public static void method2() throws ArithmeticException {
int a = 10;
int b = 0;
System.out.println(a / b);
}
//编译期的异常抛出处理
public static void method() throws ParseException {
String s = "2014-11-20";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);
System.out.println(d);
}
5.throw和throws的区别
throw:
- 用于方法体内,后面跟的是一个异常对象
- 一条throw语句只能抛出一个异常对象
- 表示抛出异常,由方法体内的语句来处理异常
- 执行throw语句一定发生了这种异常
- throw的对象为运行期异常时,不需要在方法声明上throws异常类;对象为编译期异常时,要在方法上throws出异常类
throws:
- 用于方法声明上,后面跟的是异常类
- 可以跟多个异常类,用逗号隔开
- 表示抛出异常,抛出的异常交给调用者处理
- 只是表示了发生异常的可能性,不一定真的发生这些异常
6.finally关键字
被finally控制的语句体一定会执行,除非在finally之前JVM退出了(比如System.exit(0))
作用:用于释放资源,比如IO流中、数据库操作中
final、finally、finalize辨析
final:
- 是修饰符,可以修饰类、方法、变量
- 修饰类,类不能被继承
- 修饰方法,方法不能被重写
- 修饰变量,对于基本数据类型的变量,值不变;对于引用类型的变量,存储的是地址值,即地址值不可变,但是指向的对象内容是可以改变的
finally:
- 是try...catch...finally异常处理的一部分,可以用于释放资源
- 一般被finally控制的语句都会执行
- 如果在finally之前,JVM就退出了,则不会执行
finalize:
- 是Object类中的一个方法,用于垃圾回收
catch中的return语句与finally语句执行顺序
- try...catch中由return语句时,finally中的语句仍然会执行;
- 先计算返回值保存起来,然后执行finally语句,最后回到之前的返回路径返回事先保存的值
- 如果finally中也有return语句,程序执行到finally时就会提前退出,无法再回到之前的返回路径了
7.编译期和运行期异常处理小结
- 对于编译时期异常,必须处理,否则编译不通过
- 异常代码上try...catch处理时,异常前后的代码正常执行
- 方法上抛出后由调用者(非JVM)处理,此时异常前后代码都执行
- 将异常抛给main,此时JVM默认处理,异常前代码执行,处理异常然后中断程序
- 对于运行时期异常,可以处理也可以不处理:
- 不处理,此时异常前的代码正确执行,异常代码由JVM默认处理,然后中断程序,异常后的代码不执行;
- 在方法声明上抛出给调用者,但调用者不处理或抛给虚拟机处理(抛出异常≠处理异常、虚拟机处理异常后中断程序)
- 不抛出也不处理
- 处理,则异常前后代码都会执行
- 抛出给调用者,调用者对异常进行处理
- 不抛出,直接try...catch处理
8.自定义异常
自定义异常类需要与异常体系形成联系——通过继承Exception类或RuntimeException类
- 继承自RuntimeException时,自定义的异常为运行时期异常
- 继承自Exception类时,自定义异常为编译期异常
- 自定义异常类无需编写任何成员,构造方法可写可不写
- 调用异常方法时仍然需要对异常进行处理
//自定义异常继承自Exception,属于编译时期异常,故要在方法上抛出
public void check(int score) throws MyException {
if (score > 100 || score < 0) {
throw new MyException("分数必须在0-100之间");
} else {
System.out.println("分数没有问题");
}
9.父类和子类抛出异常说明
- 如果父类某方法抛出异常,子类在重写该方法时必须抛出与父类一样的异常或该异常的子类;
- 如果父类抛出多个异常,子类重写该方法时只能抛出相同异常或异常的子类,不能父类没有的异常;
- 如果父类某方法没有抛出异常,则子类重写该方法时不能抛出异常,若子类方法体内有一场,只能用try...catch处理不能throws抛出