前言
我个人学 异常的时候还是卡了一下的,重新看了两遍视频才懂,可能我笨吧,如果没有基础建议先看视频再结合博客,我相信总能学会的
异常概述
异常也是个类,在顶层是 Throwable 类,然后 有 Error 类 和 Exception 类继承了 Throwable类,Error 就是 JVM 虚拟机无法解决的问题,程序会直接崩溃,所以 Error 我们不用去管他,而 Exception 是可以用代码解决的错误。
这里我们主要关注 Exception , Exception 分为运行时异常 和 编译时异常,运行时异常就是指 RuntimeException 下的异常,编译时异常就是Exception 子类中除了 RuntimeException 其他的所有异常
运行时异常
编译器不强制要求处理的异常,因为运行时异常会自动 throws 到调用者,这里不知道 throws 没关系,后面就知道了。
常见的运行时异常
- 空指针异常 (NullPointerException)
当某个对象为 null, 你直接访问
- 数组越界异常 (ArraylndexOutOfBoundsException)
这个大家都很熟悉了,这里不解释了
- 类型转换异常 ( ClassCastException)
试图将对象转化成不是实例的子类,例如你向上转型完,你再转成一个毫不相关的对象
- 数字运算异常(ArithmeticException)
出现异常的运算:例如 整数 / 0
- 数字格式不正确异常(NumberFormatException)
将字符串转换成一种数值类型,但该字符串不能转化为适当格式:例如 “123” 转 123 ,你搞个 “abc”,它就转不了数值类型
编译时异常
必须要处理的异常,否则代码不给过,只要知道这点就好了
最重要的点就是:运行时异常可以不处理,编译时异常一定要处理
异常处理的方式
运行时异常 如果没处理 会自动 throws 出去
编译时异常,程序必须处理,try-catch-finally 或者 throws
.
try-catch-finally
程序员在代码中捕获发生的异常,自行处理
try {
捕获到异常创建一个异常对象,传给 catch 括号的异常对象
} catch (Exception e) {
得到这个异常对象,然后程序员就可以自己处理了
} finally {
不管有没有异常发生,始终都要执行 finally 里的内容
通常我们将 释放资源 的代码,放在 finally
}
如果没有捕获到异常 catch 代码块不会执行
如果没有 finally 语法也可以通过
细节:
1. 如果异常发生了,则 try 异常块后面发生的代码不会指定,直接进入 catch 块
try {
int a = 10, int b = 0;
int c = a / b; //这里抛出了异常
System.out.println("异常发生后,try 块异常后的代码不会执行了"); //这句不会执行了
} catch (RuntimeException e) {
System.out.println("异常发生了");
} finally {
System.out.println("不管怎么样这个都执行");
}
2. 可以有多个catch语句,捕获不同的异常,要求父类异常放在后面,比如 Exception 要在 NullpointerException后面,如果发生异常,只会匹配一个catch,因为 try 发生异常后代码不执行的
int a = 10;
int b = 0;
int[] arr = new int[3];
try {
System.out.println(arr[4]); //这里抛出异常了,寻找下面匹配的 catch ,然后try块中下面的代码不执行了,直接 finally 完就没了
int c = a / b;
System.out.println("异常发生后,try 块异常后的代码不会执行了");
} catch(ArithmeticException e) {
System.out.println("发生了 除数为 0 异常");
} catch (RuntimeException e) { //Runtime 是 Arithmetic 的父类所以要放在下面
System.out.println("异常发生了");
} finally {
System.out.println("不管怎么样这个都执行");
}
System.out.println(arr[4]);这里这里的时候可以理解为 throw new ArithmeticException() 然后丢到 catch 里
是 throw 不是 throws 区别后面说
3. 可以进行 try - finally 配合使用,这种用法相当于,没有捕获异常,因此程序会直接崩掉,用于执行一段代码,不管发生异常,都必须执行某个业务逻辑
try {
int a = 10;
int b = 0;
int c = a / b;
System.out.println("异常发生后,try 块异常后的代码不会执行了"); //这句不会执行了
} finally {
System.out.println("不管怎么样这个都执行");//finally执行之后直接崩了,因为运行时异常没处理,默认 throws 了
}
throws
将发生的异常抛出,交给调用着(方法)来处理,最顶级的处理者时JVM
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,就应该显示地抛出去
细节
- 在方法声明中用 throws 语句可以声明抛出异常的列表,throws 后面的异常类型可以是方法中产生的异常类型,也可以时它的父类。
public class Main {
public static void main(String[] args) throws Exception{
f1();
}
//这里就是用了列表
public static void f1() throws FileNotFoundException,Exception{
//创建了一个文件流对象
//这里的异常时一个编译异常
//1.使用 try-catch-finally 处理
//2.使用 throws,直接抛出异常,让调用者解决
FileInputStream fis = new FileInputStream("d://test.txt");
}
}
这里有一个编译异常必须要处理,我选择直接 throws 到调用者,但是调用者不想用 try catch 处理 又throws 到 JVM ,JVM 直接寄掉
2. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
class Father {
public void method() throws RuntimeException {
}
}
class Son extends Father {
//这里重写了父类方法,抛出的异常的类型,必须和父类一致,或者父类异常的子类型
@Override
public void method() throws NullPointerException {
}
}
自定义异常
- 如果继承 Exception,属于编译异常(如果用 Excpetion 要注意它一定要被处理)
- 运行时异常我们直接继承 RuntimeExcptionn
public class Main {
public static void main(String[] args) {
int age = 180;
if( age < 0 || age > 120 ) {
throw new AgeException("年龄要大于 0 ,小于 120");
}
System.out.println("你的年龄范围正确");
}
}
//自定义异常
class AgeException extends RuntimeException {
public AgeException(String message) {
super(message); //message 一直传到 Throwable 的 detailMessage,然后输出detailMessage
}
public AgeException() {
super();
}
}
这里是 throw 不是 throws
一般情况下,自定义异常继承 RuntimeException,好处是我们可以使用默认的处理,就是 RunmtimeException 自动向上抛
至于异常为什么这么定义,可以随便弄一个异常然后追一下源码,就懂了
throw 和 throws
throw 和 throws 的区别
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
Throwable的常见成员方法:
方法名称 | 说明 | ||
---|---|---|---|
public String getmMessage() | 返回此 throwable 的详细消息字符串 | ||
public String toString() | 返回此可抛出的简短描述 | ||
public void printStackTrace() | 把异常的错误信息输出在控制台 |