Java异常处理基本知识点
什么是异常?为什么要处理异常?
我们在写代码的过程中,尽量做到尽善尽美,能不出bug就尽量不出bug.不过…程序员是一个人,既然是人就难免会出现错误,也许是因为逻辑不清晰,也许是因为考虑不全面导致最后代码写出来之后会出现用户观感不友好的情况…也就是我们所说的bug.当然,也许你的代码很优秀,自己在测试过程中没得任何问题…但是有没有可能用户并不理解你的意思?给你传入一个极度不友好的数据给你?又或者是在需要读取某个数据文件的时候…恰巧谁手闲给你删了,程序找不到这个文件?亦或者是因为网络问题给你传进的参数让你裂开?..
那么什么是异常??
在《intel开发手册》中对异常的解释为:异常是一种通常是在指令导致错误时发生的事件。
在《The Java® Virtual Machine Specification Java SE 8 Edition》(Java虚拟机规范)中对异常的解释为: Java虚拟机中的异常由可抛出类或其子类的实例表示。
在《The Java® Language Specification Java SE 8 Edition》(Java语言规范)中对异常的解释为: 当一个程序违反了Java编程语言的语义约束时,Java虚拟机就会将这个错误作为异常信号发送给该程序
所以什么是异常?
我的理解是:程序在运行过程中出现了不该出现的情况,不符合我们程序员的预期.
Java异常的分类
总所周知,Java中所有的类全部都来自于一个父类: Object
那么Java的异常类也绝对来自Object 而且在Java虚拟机规范中解释的异常是由可抛出类及其子类的实例表示…
那么说明,所有的异常均有一个除了Object这样的一个根父类之外,还应该有一个 Throwable 这样的一个类
而直接继承自这个Throwable的有两个类:
一个叫Error,一个叫Exception
Error 错误
Error,翻译过来叫错误
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
一个Error是可抛出异常的直接子类,表明是一个程序不应该试图捕获的严重的问题,什么意思呢?大家都知道,我们Java虚拟机会在运行时试图帮助我们处理掉异常,但有些问题是没办法处理的,比如: 内存溢出(可分配的空间不足以分配给程序所需要的空间),Java虚拟机栈溢出(StackOverflowError)…等等许多我们无法处理掉的问题
对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。因此我们编写程序时不需要关心这类错误。
而Error也分为很多子类,之后我将会一一列举
Exception 异常
Exception,翻译过来叫异常
The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.
Exception类及其子类是Throwable的一种形式,它指示合理的应用程序可能希望捕获的条件。什么意思呢?和Error不同的地方就是这个异常是其它因编程错误或偶然的外在因素导致的问题,是可以使用针对性的代码进行处理的
程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
同样的,Exception也有很多子类,之后会一一列举
而Exception又分为两大异常: 运行时异常(RuntimeException及其子类)和编译时异常(除了RuntimeException及其子类以外的异常)
检查时异常与非检查时异常
检查时异常: 就是在编译过程中就会给你报出的异常,有时候在编写代码的时候编译器就要求你必须对这个进行处理,也就是说代码在运行之前,就给你检查了,你不处理就不让你编译通过
非检查时异常: 在编译的时候没有强制要求你处理这个异常,但你有可能出现错误,而且这些异常不通过异常处理器去处理,而是因为你代码逻辑有问题或者本身你代码就有问题导致出错
检查时异常有哪些? 除了Error和运行时异常及其子类以外的所有异常
非检查时异常有哪些? Error和运行时异常及其子类
如何处理检查时异常呢?
- 不管,直接抛出
- 进行异常捕获
必须处理/捕获/抛出 必须!
如何处理非检查时异常?
- 继续抛
- 不鸟它
- 代码处理
- 捕获
一般代码处理掉,处理不掉就捕获或者直接抛出
代码异常信息查看
先举两个例子:
demo1: DivideByZero.java 测试除零
public class DivideByZero {
public static void main(String[] args) {
System.out.println(1/0);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RS830Asc-1657469727894)(D:\桌面\Java异常全面分析\images\image-20220710204959564.png)]
demo2: AbnormalInput.java 通过Scanner获取整数,你偏偏输入字符串
import java.util.Scanner;
public class AbnormalInput {
public static void func(){
Scanner scanner = new Scanner(System.in);
scanner.nextInt();
}
public static void main(String[] args) {
func2();
}
public static void func2(){
func();
}
}
作为程序员,你应该对这种报错信息要有一定的敏感吧,毕竟…出了错要解决吧…
因为Java的函数调用是层级的,所以只要你其中的某个位置发生了错误,那这一条线全部都会报错,注意排查
为什么会出现这一条线的报错呢?
异常是在执行某个函数时引发的,而函数又是层级调用,形成调用栈的,因为,只要一个函数发生了异常,那么他的所有的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就形成的了异常追踪栈。异常最先发生的地方,叫做异常抛出点。
异常处理机制
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。同一做出处理
异常对象的产生
两种方式: JVM自动生成异常对象 或者 开发人员自己手动在代码中new一个异常对象
自己手动创建的这个对象,如果不进行任何处理,对程序的运行并无大碍
异常的抛出(throw关键字和throws关键字)
我们的异常一般在方法中产生,那么如果这个方法处理不了,就会需要向外抛出这个异常,然后让上一级方法(也就是调用者Caller)去处理,上一级如果也处理不了,就再往上抛,直到main方法,如果还是不进行任何处理,那就程序终止吧…(是不是很像推责?是不是很像踢皮球?产生了问题不管,让其他人处理,结果就导致最终快乐地裂开…)
而throw和throws的区别是什么呢??
猜测一下?英语里的+s是干嘛?复数?也就是说抛一个用throw,抛多个用throws?咱们来试一下
那就用编译器来测,而且想要在编译器中能直观地看到错误信息,那不得用编译期异常?(我找的是IOException)
欸!一眼就看出来了,throw不能放在方法头,那我要是放在方法体里面呢?
既然要放在方法体中,那么就得使用new对象的形式来创建
这次没报错,但是有警告: 异常没有抛出
对啊,我创建了,但是没有抛出去
按照编译器给的提示,最后写成这样
那么,我们可以知道,
throw关键字存在于方法体内,用于将创建出来的异常对象抛出
throws关键字用于方法签名上,也就是方法头上,用于表示当前方法将可能需要向外抛出什么异常类
为啥要叫throw和throws呢?我觉得也挺好理解的,throw是抛出一个异常对象,throws是抛出多种异常类
那我要是不想抛出异常呢?我想自己处理掉呢??
使用try…catch进行异常捕获
到目前为止, 已经知道如何抛出一个异常。这个过程十分容易。只要将其抛出就不用理踩了。当然, 有些代码必须捕获异常。捕获异常需要进行周密的计划。
要想捕获一个异常, 必须设置 try/catch语句块。最简单的 try语句块如下所示:
public class Test {
public static void main(String[] args) {
try {
// 可能出现异常的代码区域
} catch (Exception e) { //小括号里填写可能出现的异常类及其变量
// 捕获到异常之后该怎么处理
}
}
}
如果在 try语句块中的任何代码抛出了一个在 catch 子句中说明的异常类, 那么
1 ) 程序将跳过 try语句块的其余代码。
2 ) 程序将执行 catch 子句中的处理器代码。
如果在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句。如果方法中的任何代码拋出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出,所以说: 希望调用者为这种类型的异常设计了相应的catch 子句
使用try…catch…finally进行异常捕获
当代码抛出一个异常时, 就会终止方法中剩余代码的处理,并退出这个方法的执行。
一般在这个会在需要外部资源的时候用的比较多,比如:数据库连接,文件处理等…
你打开了数据连接流,出现了异常,你总得关闭吧,不然一直占用着资源…这不太好吧…现在只是一个小demo,但是到后面大型项目了呢?毕竟Java是一个服务器后端开发语言啊,连接搞完了你得释放掉哇,不然你服务器扛不住的哇…
public class Test {
public static void main(String[] args) {
try {
// 可能出现异常的代码区域
} catch (Exception e) { //可能出现的异常
// 捕获到异常之后该怎么处理
}finally {
// 一定会执行的语句
}
}
}
finally 与 return
-
在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。
-
finally中的return 会覆盖 try 或者catch中的返回值。
-
finally中的return会直接处理掉前面try或者catch块中的异常
-
finally中的异常会覆盖掉之前try或者catch中的异常
so,
不要在finally中加return,别在这里面throw异常
==== 希望大佬评论指出问题 ❤️
==== 如果对你有帮助,小菜鸟需要鼓励,点个赞吧O(∩_∩)O😛~
本文参考内容:
《Java核心技术 卷Ⅰ》
《Introduction to Java Programming》
https://blog.csdn.net/ligonglanyuan/article/details/122714586