异常
基本概念:程序在运行过程中出现的特殊情况
异常处理的必要性:任何程序都可能存在大量的未知问题,错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。
异常即为Throwable,他是一切异常的父类,位于java.lang包下
Throwable又分为Error和Exception两个子类
Error
基本概念
指JVM,硬件,或执行逻辑错误,不能手动处理
程序员一定要(避免)Error产生
无穷递归即是一个很经典的Error错误
public static void main(String[] args) {
test();
}
public static void test(){
test();
}
Exception
基本概念
指程序在运行和配置中产生的问题,可以处理
- Exception分为CheckedException和RuntimeException两大类
- CheckedException为受查异常,必须处理或者声明
- RuntimeException为运行时异常,可以不进行处理,也可以不声明
异常的产生
一般的Exception异常都是通过throw new 异常类()来进行异常的抛出,从而产生异常
异常的声明
如果某个方法产生了异常,那么这个方法可以throws该异常名进行声明(RuntimeException可不进行声明,CheckedException如果没处理异常则必须声明)
当遇到某个方法体中遇到无法处理的异常时,也可以将其声明出去,由调用该方法的人进行处理
异常声明尽量要与throw的异常相同,这样处理时更方便快捷
异常的传递
在调用方法时,如果某个方法中产生了异常,则会立刻中断该方法,而且会顺着递归方向进行传递,如果调用这个方法的方法没有处理这个异常,则会继续中断该方法且继续传递,直到传入main方法中也没进行处理,则会中断main方法同时将异常传递进JVM虚拟机中,由JVM处理该异常并打印堆栈跟踪信息
异常的处理
异常一般情况下通过try{}catch(){}finally{}结构进行捕获处理
其中try包含的是最可能产生异常的代码块,如果在try中产生异常,则会立刻中断try后的所有代码,转而由catch捕获对应的异常并进行处理,同一结构中catch可以进行多次捕获来处理由小到大范围的异常
try {
int[] acc=new int[10];
} catch(NullPointerException n){
}catch (RuntimeException r) {
} catch (Exception e) {
} finally {
}
finally代码块中的代码一定会被执行到,所以常用来释放空间和垃圾回收
如果try,catch和finally代码块中都含return语句,则无论什么情况都会返回finally中的return值,具体实现机制可看本文最后
处理方案一般情况下可分为三种:catch(Exception e){ 处理方案 }
1.sout(“你想说出的话”) //通过自己的语句警告
2.e.printStackTarce() //打印堆栈信息
3.e.getMessage() //打印异常中的Message属性存储的信息
值得一提是,如果你想使用第三种方法来处理,则必须抛出异常时,调用的是有参的构造方法,即Exception对象中已经含有Message值了,这样你就可以打印出Message值了
以下为Throwable类中构造方法:
private String detailMessage;
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
public String getMessage() {
return detailMessage;
}
以下为其子类的构造方法:
public Exception(String message) {
super(message);
}
带有异常声明的方法覆盖
当你自己定义了一个类或接口且该类中含有声明异常的方法时,如果你想继承这个类或者实现这个接口,注意:
如果你重写后的方法想继续抛出异常,则异常声明范围必须小于等于父类本身方法声明的异常
class Father{
public void test() throws Exception {
throw new Exception();
}
}
class Son extends Father{
public void test() throws Exception{
throw new Exception();
System.out.println("override");
}
}
这是为了防止多态调用所产生的问题
自定义异常类
如果某些情况下,你发现抛出即声明异常不能产生望文生义的效果,你可以自定义异常类,如下图:
class NumberNotEqualTenException extends Exception{
}
则当你某个方法需要用的该异常时则可throw相应异常
public void setNumber(int n) throws NumberNotEqualTenException {
Scanner sc = new Scanner(System.in);
int num = n;
if (num == 10){
throw new NumberNotEqualTenException();
}
}
你就可以在调用时看到效果了
异常中经典的Return问题
当你碰见如下代码时,你会认为返回值是多少?
public int test(){
int a = 10;
try {
a = 20;
throw new Exception();
} catch (Exception e) {
a = 30;
return a;
} finally {
a = 40;
}
}
答案是30,具体处理机制如下图
上图为JVM虚拟机处理字节码文件,我们可以看见,当执行到iload_0时,即准备执行return语句,但由于有finally代码块的存在,必须执行其中的内容,所以又临时将存储在栈顶的30放在了局部变量3中,直到finally代码块执行完毕后,iload_3将30加载到栈顶后ireturn返回
上图右边部分又被称为栈帧结构,存在于JVM栈中,每一个栈帧结构对应一个方法
从此图也可以看出方法的执行顺序,每当一个栈帧结构进栈时执行,而当另一个栈帧结构进栈则先执行,即优先执行栈顶元素
本文章仅供个人参考学习,欢迎各位大佬交流与改正