一、概述
- 异常是一套用于反馈和处理问题的机制
- Throwable 是所有异常和错误的顶级父类
- Throwable派生出两个类Error和Exeption
二、Error
- 出现在代码逻辑没有错误,因外界因素或环境因素导致的严重错误的情况。
- 一旦出现,无法修改
三、Exception
- -异常,代表了程序在编译和运行时发生的各种不期望发生的事件。是java异常处理的核心。
- 非检查是异常(运行时异常):
- -编译时不会报错,运行时报错,往往是语法正确,但是逻辑错误。
- 编译前可以处理,也可以不处理。
- 一个小的父类 --- RuntimeException
- 检查异常(编译时异常):
- 编译时已经出现,一旦出现,则必须处理
- 要么抛出,要么捕获
四、异常的捕获
public static void main(String[] args) {
// System.out.println(1 / 0);
try {
int[] arr = new int[3];
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println();
}
}
- 多个异常可以同时处理,用多个catch捕获
- 如果所有异常的处理方式都一样,可以用统一的Exception一次捕获
- 多个异常分组处理,在同一组异常之间可以用"|"隔开。(从JDK1.7开始)
try {
String msg = readFile(null);
System.out.println(msg);
} catch (PathNotExistException | FileCannotReadException e) {
// 打印栈轨迹
e.printStackTrace();
// System.out.println(e.getMessage());
} catch (Exception e) {
} finally {
System.out.println("文件释放");
}
- 如果方法抛出的是父类异常,则必须要一以父类异常捕获
- 在捕获异常 时,要求先捕获子类异常,在捕获父类异常。否则子类异常将永远无法捕获到。
五、排查异常
- 查看异常的名字
- 查看异常信息
- 从栈轨迹尾端开始看起(控制台从上至下)。
注意:如果异常最终抛给了JVM,那么JVM默认打印 这个异常的栈轨迹。
六、异常的自定义
- 当一个类继承Exception,则默认是编译时异常
- 当一个类继承RuntimeException时,才是运行是异常。
- 一个类继承运行时异常类,那么这个类就是运行时异常;反之,则为编译时异常。
public class Demo_01 {
public static void main(String[] args) {
try {
String str = readFile("C:\\ a.txt");
} catch (FileCannotReadException e) {
//打印异常信息
System.out.println(e.getMessage());
//打印栈轨迹
e.printStackTrace();
//查看异常: 1. 查看异常名字 2. 查看异常信息 3. 从栈轨迹尾端开始排查(控制台中从上致下)
//如果异常最终抛给了JVM,JVM默认打印这个异常的栈轨迹
}
}
private static String readFile(String string) throws FileCannotReadException {
//如果是是以C开头,则认为这个文件不可被读取
if(string == null){
//运行时异常,可以处理,也可以不处理
throw new NullPointerException("路径不能为空");
}else
if(string.startsWith("C")){
//编译时异常一定要处理
throw new FileCannotReadException("文件不能被读取");
}
return null;
}
}
//当一个类继承Exception时,默认是编译时异常
//如果一个类继承的时RuntimeException,才会称为一个运行时异常
//一个类继承编译时异常,则这个类时编译时异常。反之为运行时异常。
class FileCannotReadException extends Exception{
public FileCannotReadException() {}
public FileCannotReadException(String msg){
super(msg);
}
}
七、异常方法的重写和重载
- 异常不会影响方法的重载。当一个类中方法名一致,但参数列表不一致时发生方法的重载,方法的重载和修饰符、返回值类型、异常都没有关系。
- 在重写父类的异常方法时,子类重写的方法抛出的异常不能超出 父类异常方法抛出异常的范围(编译时异常的要求)。方法重写时必须遵循两小两大一小原则。(参考:http://blog.csdn.net/chou_out_man/article/details/77976679)
- 子类不能抛出范围比父类 更大的异常 --- 针对的是编译时异常 , 但对运行时异常 。
class A {
public void m() throws IOException {
}
public void m(int i) throws SQLException {
}
}
class B extends A {
// 子类不能抛出比父类更多的异常。
// 在方法重写的时候,子类中重写的方法抛出的异常不能超过父类。---要求的是编译时异常
public void m() throws EOFException, FileNotFoundException, NullPointerException {
}
}
八、finally关键字的运用
- 无论如何都会执行一次。往往用于一些资源需要回收等一系列 善后处理。
public static void main(String[] args) {
try {
Scanner s = new Scanner(System.in);
System.out.println(s.nextInt() / s.nextInt());
s.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 无论代码中出现异常与否,都会执行一次。
System.out.println("执行完毕");
}
}
- 无论try的代码中是否有异常,finally中的代码都会执行一次。
- finally执行在try中return和异常发生之前。
- 补充案例:
public static void main(String[] args) {
System.out.println(m());
}
@SuppressWarnings("finally")
private static int m() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
try {
return 4;
} finally {
return 5;
}
}
} // 结果是5
public static void main(String[] args) {
System.out.println(m());
}
private static int m() {
int i = 5;
// 对于程序而言,是从上到下从左到右依次编译运行的。
try {
// return 5;-> i->6
return i++;
} finally {
// i = 6; -> 7
i++;
System.out.println("Finally:" + i);
}
}
public static void main(String[] args) {
System.out.println(m());
}
public static Person m() {
Person p = new Person();
try {
p.setName("翠花");
p.setAge(16);
// return p; -> 实际上返回的是p的地址
return p;
} finally {
p = new Person();
p.setName("如花");
p.setAge(18);
}
}