希望通过博客和大家相互交流,相互学习,如有错误,请评论区指正
文章目录
-
- 一、什么是异常?
-
- Java的异常体系
- 如何排查异常
- 二、 处理异常
-
- try…catch基本语法
- 这个时候就要处理异常
- 如果我们用Exception来捕获异常呢?
- finally
- 在方法中出现异常
- 异常处理流程
- 手动抛出异常
- 三、自定义异常
-
- 源码剖析
- 自定义
- 注意
如下实例:
public class Demo {
public static void main(String[] args) {
int num = 2/0;
}
}
这段代码中“除0”的逻辑在C语言中就只是报个警告,但是Java是比较安全的语言,在编译运行的时候会直接抛出异常
那么到底什么是异常?
一、什么是异常?
异常指的是在程序运行过程中发生的异常事件,通常是由外部问题(如硬件错误、输入错误)所导致的。在Java等面向对象的编程语言中异常属于对象.
异常本身是一个对象,产生异常就是产生了一个异常对象
Java的异常体系
我们都知道Java中所有类都是继承自Object的,Throwable这个类也是如此,如下Java官方文档
我们可以看到
继承于throwable类的有两个,一个是Error(错误),一个是Exception(异常),throwable是Java中所有错误和异常的父类
而异常又分为受查异常和非受查异常(我们之前碰到过的除0发生的异常就是非受查异常)
非受查异常:Java语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为非受查异常(因为程序的代码Bug导致的问题,空指针异常,数组下标越界异常)
受查异常:所有的其他异常称为 受查异常(程序编译时抛出的异常),必须处理,否则代码编译不能通过(一些可以经过重试或者程序自动修复可以解决的问题)
错误:因为设备或其他硬性环境导致的,程序根本无法修复的问题
如何排查异常
如下代码:
在文章开头写的一个"除0"代码的运行结果:
这里面java.lang.ArithmeticException是异常的种类,by zero是异常的具体信息
常见的异常种类有:
NullPointerException:空指针异常
ArithmeticException:算术异常
ArrayIndexOutOfBoundsException:数组下标越界异常
PS:异常的种类有很多,不同的异常具有不同的含义,也有不同的处理方式
有的时候异常信息会有很多行,这些异常信息被称为异常信息栈/异常跟踪栈,那么我们如何在这些异常信息中找到引发异常的第一现场呢?
直接点击最上面第一条异常信息的蓝色部分,光标就会自动跳转到引发异常的地方,从而进行相应的修改
二、 处理异常
Java当中异常的核心思想其实就是让我们先操作,在操作过程中遇到问题再处理
try…catch基本语法
try{
有可能出现异常的语句;
}[catch (异常类型 异常对象) {
捕捉try当中可能出现的异常;
可以写多个catch;
} ... ]
[finally {
异常的出口;
可以不写;
finally中的代码一定会被执行,用来做一些善后工作;
}]
- try 代码块中放的是可能出现异常的代码.
- catch 代码块中放的是出现异常后的处理行为
- finally 代码块中的代码用于处理善后工作, 会在最后执行.
- 其中 catch 和 finally 都可以根据情况选择加或者不加.
我们还是以"除0"问题为例
public static void main(String[] args) {
int a = 10/0;
System.out.println("666");
}
这个程序显然到了int a = 10/0;
就会抛出异常,后面的666
不会被打印出来
此处发生异常,程序会直接交给JVM处理异常,导致的结果是程序会立即停止,不再向下执行
那么我们如果想要让程序继续往下执行呢?
这个时候就要处理异常
public static void main(String[] args) {
try {
int a = 10 / 0;
} catch (ArithmeticException e) {
e.printStackTrace();
}
System.out.println("666");
}
在try中放入可能会引发异常的语句,在catch后面的圆括号内写入想要捕获异常的种类ArithmeticException
,然后就可以对此异常做出处理,e.printStackTrace();打印异常追踪栈,最后再打印666
运行结果:
当程序抛出异常的时候,由catch块进行捕获,程序自己来处理异常,导致的结果就是程序会继续向下执行
注意:如果catch中要捕获的异常种类和实际发生异常的种类不一样的话,就还是交给JVM处理了,程序立即停止
如果我们用Exception来捕获异常呢?
如下代码
public static void main(String[] args) {
try {
int a = 10 / 0;
}catch (Exception e) {
System.out.println(999);
}catch (ArithmeticException e) {
System.out.println(888);
} // 直接捕获Exception的话,后面的这些catch就没啥用了,编译器就会报错
System.out.println("666");
}
所有异常继承于Exception,那么当出现异常的时候,Exception可以捕获所有的异常,这样只需要写一个捕获Exception的catch就行了,但是不建议这么写,这样得不到具体的异常种类
finally
无论catch有没有捕获到异常,finally块中的代码都会在最后被执行
public static void main(String[] args) {
try {
int a = 10 / 0;
System.out.println("666");
}catch (ArithmeticException e) {
System.out.println("888");
}finally {
System.out.println("999");
}
System.out.println("777");
}
在方法中出现异常
如果本方法中没有合适的处理异常的方式,就会沿着调用栈向上传递
public class ExceptionLearning {
public static void demo() {
int a = 10/0;
}
public static void main(String[] args) {
demo();
}
}
运行结果:
在demo方法中执行时,抛出算术异常,因为demo方法是被main方法调用的,所以demo就会让main方法来处理异常,但是main方法中也没有处理异常,就会交给JVM来处理,程序就会异常终止
若对异常做出处理
public class ExceptionLearning {
public static void demo() {
int a = 10/0;
}
public static void main(String[] args) {
try {
demo();
}catch (ArithmeticException e) {
e.printStackTrace();
}finally {
System.out.println("继续向下执行");
}
}
}
在demo方法中执行时,抛出算术异常,因为demo方法是被main方法调用的,所以demo就会让main方法来处理异常,main方法中对异常做出处理,程序继续向下执行
异常处理流程
- 程序先执行 try 中的代码,如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码,如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理异常, 就继续向上传递,一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止
手动抛出异常
除了Java编译器来抛出异常以外,我们也可以自己针对一些情况来抛出异常
三、自定义异常
要知道,异常本身就是一个对象,它肯定是对应一个类的,那么我们也可以通过创建类的方式来自定义异常
源码剖析
我们这里以Arithmetic类为例,可以先看看它的源码
从源码中会发现Arithmetic这个类继承自RuntimeException类,并且有两个构造方法,一个有参,一个无参
自定义
我们也可以仿照Arithmetic来自定义,如下代码
class MyException extends RuntimeException{
public MyException() {
super();
}
public MyException(String str) {
super(str);
}
}
public class ExceptionLearning {
public static void main(String[] args) throws MyException{ //在方法上加上异常说明, 相当于将处理动作交给上级调用者
int b = 0;
if (b == 0) {
throw new MyException("b == 0");
}
}
}
运行结果
注意
当我们将MyException
继承于RuntimeException的时候,这个异常就默认是非受查异常;继承于Exception的时候,这个异常就默认是受查异常
Java针对受查异常,强制要求: 一个方法如果抛出了受查异常,则必须通过throws声明异常