二、异常的继承体系结构
Throwable 类是 Java 语言中所有错误或异常的超类。
- Throwable类是所有错误和Java中异常的超类。 只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者Java throw 语句抛出。
- Exception和Error都是继承了Throwable类,它是异常处理机制的基本组成类型。
- Exception和Error体现了Java平台设计者对不同异常情况的分 类。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
- Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
1、Error
Error 是 Throwable 的子类,用于指示合理的应用程序 不应该试图捕获的严重问题 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
2、 Exception
异常:java语言中,将程序执行中发生的不正常情况称之为异常, 就是程序执行的时候出现了你不想看到的情况。
Exception 异常主要分为两类
- 一类是 受查异常(检查性异常) ,checkedException。可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。Checked Exception的假设是我们捕获了异常,然后恢复程序。但是,其实我们大多数情况下,根本就不可能恢复。这也是为什么有些资料说 Checked Exception 是多余的原因。
- 一类是 运行时异常(非受查异常),RuntimeException。通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。
举个例子:你要去机场和你的领导去出差,导致你最后无法坐上飞机的因素有哪些?
1、可以自行解决的:比如没带身份证,比如起床起晚了。 这叫运行时异常,通常自己导致的。你可以提前做好准备。
2、不能自行解决的:比如在你去的路途中地震了,突然下雨飞机 不能起飞了。 这叫错误,通常是上帝导致的。
中途堵车了,你领导没来。 这叫检查行异常,通常是别人导致的, 你必须提前做好措施。
(1) 常见的检查性异常
名字 | 问题 |
IOException | IO异常,在对流操作时有可能会出现的异常,学流时用到 |
SQLException | SQL异常,学数据库时会用到 |
ClassNotFoundException | 找不到某个类时,会抛出该异常, 反射时会用到 |
InterruptedException | 当阻塞方法收到中断请求的时候就会抛出中断异常 |
ClassNotFoundException
Java支持使用Class.forName方法来动态地加载类,任意一个类的 类名如果被作为参数传递给这个方法都将导致该类被加载到JVM内 存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException异常。
Class.forName("com.ydlclass.Dog");
InterruptedException
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
如果我们有一个运行中的软件,例如是杀毒软件正在全盘查杀病 毒,此时我们不想让他杀毒,这时候点击取消,那么就是正在中断一个运行的线程。
其实这段代码的大致意思就是我睡的好好的,有人把我吵醒了。一般是一个程序在睡觉,另一个线程将他打断。
(2) 常见的非检查性异常:
名字 | 问题 |
NullPointerException | 空指针引用异常,视图调用空对象的方法或者属性时,抛出该异常 |
ArithmeticException | 算术运算异常 |
ClassCastException | 类型转换异常,当试图将对象强制转换为不是实例的子类 时,抛出该异常 |
IndexOutOfBoundsException | 下标越界异常 |
NumberFormatException | 数字格式异常 |
结局此类异常通常通过简单的逻辑判断就可以,一般不需要捕获处理:
NullPointerException
String str = null;
byte[] bytes = str.getBytes();
//解决方法:
String str = null; if(Objects.nonNull(str)){
byte[] bytes = str.getBytes();
}
ArithmeticException
int i = 0;
int num = 1/i;
//解决方法:
int i = 0; if(i != 0){
int num = 1/i;
}
ClassCastException
Animal aniaml = new Dog(); Cat cat = (Cat)animal;
//解决方法:
Animal aniaml = new Dog();
if(animal instanceof Cat){
Cat cat = (Cat)animal;
}
IndexOutOfBoundsException
int[] nums = new int[3]; int i = 3;
nums[i] = 4;
//解决方案:
int[] nums = new int[3];
int i = 3;
if(i > -1 && i < nums.length){
nums[i] = 4;
}
NumberFormatException
String str = "abc";
Pattern pattern = Pattern.compile("^[-+]? [d]*$");
if(pattern.matcher(str).matches()){
int num = Integer.parseInt(str);
System.out.println(num);
}
(3) 自定义异常类型
Java 的异常机制中所定义的所有异常不可能预见所有可能出现的错误,某些特定的情境下,则需要我们自定义异常类型来向上报告某些错误信息。
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承RuntimeException 类。
一个异常怎么抛出: throw new RuntimeException(“您输入的数字不合法”);
public class Test1 {
public static void main(String[] args) {
while (true){
fun2();
}
}
public static void fun2(){
try {
fun();
}catch (IndexOutOfBoundsException e){
}
}
public static void fun(){
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();
if(i > 5){
System.out.println("我们可以继续玩游戏!");
} else {
throw new RuntimeException("我的数组下标越界了!");
}
}
}
工作中我们使用最多的还是自定义运行时异常:
public class ServiceException extends RuntimeException{
// 错误码
private Integer code;
// 错误信息
private String message;
// 空构造
public ServiceException(){}
public ServiceException(String message, Integer code){
this.message = message;
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
在需要抛出这个异常的地方,我们可以这样:
throw new ServiceException(102,"业务出问题了");
3、异常链
一个异常被抛出去后会继续被调用这个方法的方法捕获或抛出,也就是异常会扩散。
我们举一个例子:
你女朋友要找你约会——》半路遇上了债主——》债主打了你一顿——》结果约会迟到了——》你女朋友说你不爱他——》然后分手了
一件很正常的约会,因为出现了债主,导致最后两人分手,虽然这确实不可思议。一个异常的出现可能会导致整个方法调用链的问题。
方法在栈空间被调用,多个方法互相调用形成了调用链条,会生成一个StackTrace,也叫栈轨迹,或堆栈信息,或调用链。JVM抛出 异常时会打印这个堆栈信息,也就是我们经常在idea中看到的红色 的错误的信息。但是事实上,我们在捕获异常后也要主动打印堆栈信息,这样才能争取的找到问题、解决问题。
为了打印这个堆栈信息,Java每实例化一个Exception,都会对当时 的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。以建议仅捕获有必要的代码段, 尽量不要一个大的try包住整段的代码。
有时我们甚至有这样的需求:捕获一个异常后需要抛出另外一个异常,并且希望把异常原始信息保存下来,这样的多个异常组成的链条被称为【异常链】。
1. 在JDK1.4以前,程序员必须自己编写代码来保存原始异常信息,
2. 现在所有Throwable的子类子构造器中都可以接受一个cause对象作为参数,这个cause就异常原由,代表着原始异常,即使在当前位置创建并抛出行的异常,也可以通过这个cause追踪到异常最初发生的位置。
3. Throwable类及其所有的子类都提供了带cause参数的构造器, 其他的异常类就只有通过initCause()来设置cause了。