【JavaSE】异常
异常的介绍
异常,顾名思义,就是指非正常的,在预期之外的情况。
生活中:比如邮寄一个快递可收件人联系不上,这就是生活中的一个异常。
程序中:在程序运行过程中,各种异常稍不注意就会发生,从而影响程序的正常流程
常见异常如:
- 文件找不到
- 网络连接失败
- 用户输入了非法数据。
注意:异常指的是在程序运行中出现的错误,而不是语法问题导致的编译错误。
异常的分类
在Java的面向对象世界中,异常当然也是对象,众多的异常对象就描述了各种不同的异常情况。
Java 的异常有一个顶层父类Throwable,所以的异常都继承自它,在Throwable下又分两大类,一个是 Error,一个是 Exception。
Error类即其子类是指我们程序处理不了或者说不该由程序处理的错误。
这里的错误往往代表JVM在运行过程中出了问题,比如:栈溢出错误、内存不足错误
Exception类即其子类是指程序中可以处理的异常,我们平时最常打交道的就是这种异常。
它下面又分为两大类:
- 一类是RuntimeException类和其子类
- 一个是非RuntimeException类
这两种类型的区别就是:
- RuntimeException 类型不强制你手动处理,比如我们最常见的空指针异常,你无需 try...catch 也无需 throws 代码也能编译成功
- 非RuntimeException 类的异常如果你不手动处理则会编译失败比如IO异常。
这里延伸出一个概念:那就是受检异常和非受检异常。顾名思义受检异常就是必须手动处理的异常,非受检异常就是不强制程序处理的异常。除了 RuntimeException 外。Error 也属于非受检异常
异常的处理方式
在Java中有两种处理异常的方式:一个是try...catch 一个是 throws
try...catch
它就是我们常说的捕获异常
// 可以 catch一个也可以 catch多个
try{
// 可能会发现异常的逻辑
} catch(IOException e){
// 发生 IOexception时,执行此代码块
} catch(ClassNotFoundException e){
// 发生 ClassNotFoundException时,执行此代码块
} catch(Exception e){
// 发送其他异常时,执行此代码块
// 父类异常应该放在子类异常后面,否则子类异常不会被捕获
}
我们可以将可能发送异常的代码,放到 try 代码块中,然后使用 catch来捕获对应的异常。如果 try 代码块正常执行,那 catch就不会生效。如果发生了指定的异常,则会执行对应的catch代码块,然后继续往下执行,如此一来,我们就能避免异常影响到我们的正常逻辑
捕获异常时还可以接上finally 代码块,无论发布发生异常finally 代码块都会执行
throws
处理try...catch外还可以使用 throws来处理异常,在方法上使用 throws关键字可以声明该方法可能抛出的异常
// 可以 throws声明一哥异常,也可以声明多个
public static void process() throws IDException,ClassNotFoundException{
// 可能发送异常的逻辑
}
当我们调用一个方法时,如果这个方法用 throws关键字声明了受检异常
public static void process() throws IOException,ClassNotFoundException{
// ...
}
public static void f1(){
process();// 编译失败,因为没有处理受限异常
}
public static void f2(){
try {
process();//编译成功
} catch (IOException | ClassNotFoundException e){
// ...
}
}
public static void f3() throws IOException,ClassNotFoundException{
process();// 编译成功
}
此时我们就必须得手动处理它声明的异常,否则就会编译失败。要么你就 try...catch要么你就在当前方法使用 throws声明同样的或其父类异常。
这里其实就可以体现出throws的作用了
那就是我不想处理这个异常时,我可以把问题往外抛,谁调用我谁就来处理,就好像在工作中出现了一个问题:你可以选择将这个问题自行解决,也可以将这个问题丢给你的上级解决,而你的上级碰到问题时,也会面临同样的选择:要么他自己解决,要么他将问题抛给更上级。
如果一个问题或者是一个异常,发生后就一直往上抛。
public static void main(String[] args)throws Exception
{ // 如果发生异常,则程序终止
}
到最顶层的main 方法了都没人去try...catch 解决,那程序就会终止运行
调用时机
一个很简单的原则:
try{
process();
} catch (Exception e){
// ...
}
// 上面发送异常,这里也能执行
xxx();
如果当前方法需要继续运行下去,就肯定得用 try...catch,当前方法不需要继续运行就可以选择throws
建议:尽量选择捕获异常
异常必须丢给上一层去解决,这种情况毕竟在少数。
这也是为什么在设计自定义异常时都强烈建议继承RuntimeException,因为这会让你省去很多麻烦
自定义异常
Java 标准库中提供了非常多的异常类型,用来表达各种异常情况。
public class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
public MyException(String message,Throwable cause) {
super(message,cause);
}
public MyException(Throwable cause) {
super(cause);
}
}
然而在真实开发中这些异常并不能完全满足,我们的需求因为标准库的异常往往表达的是技术层面,而不是业务层面,像账号密码错误这种情况就不太适合用标准库的异常。所以在开发中,我们会自定义异常类型,来表达符合我们业务的异常情况。
只要主动继承异常类就可以定义我们自己的异常,上面说过强烈建议大家继承 RuntimeException
在自定义异常时,还强烈建议大家照着父类学习,提供多个构造方法,这样就可以传递错误信息和堆栈信息
异常自定义好后我们就可以 new出异常对象,并用 thorw关键字来主动抛出异常
thorow new MyException();// 直接抛出异常
thorow new MyException("程序崩溃");// 抛出异常的同时,传递异常信息
thorow new MyException(new NullPointerException());// 抛出异常的同时,传递其他异常的堆栈信息
注意:thorw和 thorws关键字只有一个字母之差不要弄混
throw | throws | |
---|---|---|
位置 | 代码块中 | 方法声明上 |
作用 | 主动制造并抛出异常 | 方法声明上 |
作用位置 | 代码块中 | 声明方法可能会发送的异常 |
主动抛出异常
public static void process(String arg) {
// ...业务逻辑
}
假设我们有一个接受String参数的方法,方法中会对该参数进行一些逻辑处理。
正常的业务流程要求,不允许null值出现,可如果调用者传递了一个null值进来。
此时我们该怎么做呢?
- 第一种做法就是给参数设置一个默认值,然后继续执行后续逻辑
public static void process(String arg) {
if(arg == null) {
arg = "默认值";
}
// ...继续执行业务逻辑
}
// 就好像你非法进入一个地方,保安发现了你,不光没赶你走还贴心地给了你一个身份牌,让你可以继续前行
- 第二种做法就是直接结束方法,不执行后面逻辑,这里还可以返回不同的值,来表达方法执行的结果
public static boolean process(String arg){
if(arg == null){
return false;
}
// ...业务逻辑
return true;
}
// 就好像保安发现了你,就不让你继续前进了
- 第三种做法就是直接抛出异常
public static void process(String arg) {
if(arg == null) {
throw new MyException();
}
// ...业务逻辑
}
//这个就比较猛了,就好像保安发现了你,不光不让你继续前进,还把你打了一顿。
//要是你没有捕捉异常,你就会被保安打到shi(程序终止)
使用情况
如果遇到了会影响正常逻辑的情况,基本就这三大类处理方式
知道各个方式的特点后,其实就能根据自己的需求,来做响应的处理了。
-
如果问题能够解决,并且你也愿意解决,那就让逻辑继续执行
-
如果问题无法解决,或者你不愿解决,但又不至于抛哥异常给调用者,就可以直接结束方法
-
如果问题非常严重,严重到需要警告调用者,就可以抛出异常
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示