目录
(一)什么是异常
(1)定义
在
Java
中,将程序执行过程中发生的
不正常行为
称为异常,如算术异常,空指针异常等...
(2)体系结构
如下图,Throwable是java异常体系中的顶层类,其派生出两个子类,Error和Exception
Error:是指jvm无法解决的严重问题,如栈溢出,堆溢出等...
Exception:是指
异常产生后
可以通过代码进行处理
,使程序继续执行的异常情况,异常又分为受检查异常(编译时发生的异常)和非受查异常(运行时发生的异常)
(二)异常处理
异常处理五大关键字:
throw、throws
,
try、catch、final。
(1)异常抛出
1.1Throw
在编写程序时,如果程序中出现错误,此时就需要将
错误的信息告知给调用者
,可以借助
throw
关键字,
抛出
一个指定的异常对象,将错误信息告知给调用者。
注意点1.throw 必须写在方法体内部(花括号内)2. 抛出的对象必须是 Exception 或者 Exception 的子类对象4. 如果抛出的是编译时异常(受查异常),用户必须处理,否则无法通过编译5. 异常一旦抛出,其后的代码就不会执行
1.2Throws
当方法中抛出的是编译时异常(受检查异常),用户不想处理该异常,此时就可以借助
throws
将异常抛 给方法的调用者来处理。即
当前方法不处理异常,提醒方法的调用者处理异常。
(就是说假如我手上有个手机有问题,但我不想自己解决这个问题,我就用throws把这个问题抛出,交给想用这个手机的人去解决这个问题)
注意点1. throws 必须跟在方法的参数列表之后2. 声明的异常必须是 Exception 或者 Exception 的子类3. 方法内部如果抛出了多个异常, throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型 具有父子关系,直接声明父类即可。
下代码为method1方法不想处理IOException(受查异常)使用throws抛出,主方法调用时,使用的是try ——catch方法进行处理(修手机),另外主方法也可以选择继续将异常抛出(继续将问题手机传给下一个人)
import java.io.IOException;
class Mehod{
public void method1() throws IOException {//我不想处理这个编译时异常,就用throws把异常抛出
int a = 10;
if (a == 10){//定义如果变量a的值为10则抛出编译时异常
throw new IOException("抛出了编译时异常");//不抛出会报错
}
}
}
}
public class Main {
public static void main(String[] args) {
Mehod mehod = new Mehod();
try {
mehod.method1();//不处理会报错,这里选择try catch处理
} catch (IOException e) {
e.printStackTrace();
}
}
}
下图为主方法选择throws继续抛出异常处理,但其实下方代码并不合理,这是因为main方法是程序的入口点,它的执行终止意味着整个Java应用程序的结束,所以在main方法一般是不能用throws抛出异常的
(2)异常捕获处理
2.1try-catch
try: try中放的是可能会出现异常的代码
catch: 如果try中的代码抛出异常了,catch捕获时异常类型与try中抛出的异常类型一致时,或者catch捕获时异常是try中抛出异常的父类时,就会被捕获到,然后对异常进行处理,处理完成后,跳出try-catch结构,继续执行后序代码
下方代码是try-catch的具体语法规范
public class Main {
public static void main(String[] args){//选择继续抛出异常
Thread t = new Thread(()->{
try {//try中放的是可能会发生异常的代码
Thread.sleep(1000);
} catch (InterruptedException e) {//catch括号中用于捕获抛出的异常
//方法体中是对异常进行处理的代码
e.printStackTrace();
}
});
}
}
注意点
1. try 块内抛出异常位置 之后的代码 将不会被执行2. 如果抛出异常类型与 catch(捕获) 时异常 类型不匹配 ,即异常 不会被成功捕获 ,也就不会被处理,继续往外抛,直到 JVM收到后中断程序。3. try 中可能会抛出多个不同的异常对象,则必须用多个 catch 来捕获4.如果异常之间具有父子关系,一定是子类异常在前 catch ,父类异常在后 catch(父类在前catch的话异常都被父类捕获完了,就没子类什么事了)
2.2finally
有些
特定的代码
,不论程序是否发生异常,都
必须要执行
,比如程序中资源的关闭
:网络连接、数据库 连接、IO
流等,
在程序正常或者异常退出时,必须要对资源进进行回收
。另外,因为
异常会引发程序的跳转,可能
导致
有些语句执行不到
,
finally
就是用来解决这个问题的,因为在finally中的代码无论是异常还是正常,都会被执行到。
如下图try中的代码抛出的算术异常没有被catch正确捕获,后面的打印操作不会被执行到,但finally中的代码,一定会被执行到,所以
一般是在finally中执行比较重要的资源管理工作
。
public class Main {
public static void main(String[] args){//选择继续抛出异常
Mehod mehod = new Mehod();
try {
int a = 10/0;//抛出的是算术异常
} catch (ClassCastException e) {
e.printStackTrace();
}finally {
System.out.println("一定会被执行到");
}
System.out.println("此处异常没被正常处理不会执行到");
}
}
注意点1.finally 执行的时机是在 方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行finally). 但是 如果 finally 中也存在 return 语句 , 那么就会执行 finally 中的 return, 从而 就不会执行到 try 中原有的 return, 所以一般不会在finally中使用return.2.一般情况下只要不出现极端情况(jvm崩溃,操作系统错误等)finally块中的代码就一定会被执行到。
2.2异常处理流程
1.程序先执行
try
中的代码
2.如果
try
中的代码出现异常
,
就会结束
try
中的代码
,
看和
catch
中的异常类型是否匹配
.
3.如果找到匹配的异常类型
,
就会执行
catch
中的代码
4.如果没有找到匹配的异常类型
,
就会将异常向上传递到上层调用者
.
5.无论是否找到匹配的异常类型
, finally
中的代码都会被执行到
(
在该方法结束之前执行
).
6.如果上层调用者也没有处理的了异常
,
就继续向上传递,
一直到 main
方法也没有合适的代码处理异常
,
就会交给
JVM
来进行处理
,
此时程序就会异常终止
.
方法之间是存在相互调用关系的
,
在
JVM
中有一块内存空间称为 "虚拟机栈
"
专门存储方法之间的调用关系
.
当代码中出现异常的时候
,
我们就可以使用
e.printStackTrace();
的 方式查看出现异常代码的调用栈.
如下图中从下向上看,可发现是main方法调用的method1方法
(三)自定义异常
有时候根据需要,我们也可以自定义一些异常类
(1)步骤
1.
自定义异常类,然后继承自
Exception
或者
RunTimeException
2.
实现一个带有
String
类型参数的构造方法,参数含义:出现异常的原因
如下图是定义一个自定义异常的语法,及使用
class MyException extends RuntimeException {//自定义一个运行时异常
public MyException(String message) {
super(message);
}
}
public class Main {
public static void main(String[] args) {//选择继续抛出异常
int a =10;
if (a == 10){
throw new MyException("老皇甫自己的异常");
}
}
}
注意点
1.自定义异常通常会继承自 Exception 或者 RuntimeException2.继承自 Exception 的异常默认是受查异常3.继承自 RuntimeException 的异常默认是非受查异常