Slient Blog

Java异常机制

2018-04-11

异常类

Java中处理异常的两种方式

  • 捕获异常:就地解决,并使程序继续执行 //积极的处理方式

    当Java运行时系统得到一个异常对象时,他将会沿着方法的调用栈逐层回溯,寻找处理这一异常的代码。找到能够处理这种类型的异常的方法后,运行时系统把当前的异常对象交给这个异常方法后,这一过程称为捕获(catch)异常。如果Java运行时系统找不到可以捕获异常的方法,则运行时系统中将终止,相应的Java程序也将退出。

  • 声明抛弃异常:将异常抛出方法之外,由调用该方法的环境去处理 //消极的处理方式

    如果在一个方法中生成了一个异常,但是这一方法并不确切地知道该如何对这一异常事件进行处理,这时,一个方法就应该声明抛弃异常,使得异常对象可以从调用栈向后传播,直到有合适的方法捕获它为止。

    Java中预定义的异常类

    预定义的异常类

    Error

    Error标识不可能或难以恢复的严重问题,例如内存不足,程序一般不处理这类情况。

    RuntimeException

    RuntimeException指示设计或实现上的问题,如果程序正确运行,这样的情况是不应该出现的。

    常见异常

  • NullPointException–空指针异常
  • ArrayIndexOutOfBoundsException–数据越界异常
  • NegativeArraySizeException–数组负下标异常
  • ArithmeticException–算数异常类
  • ClassCastException–算数运算异常
  • IllegalArgumentException–传递非法参数异常
  • ArrayStoreException–向数组中存放与数组类型不符元素异常
  • NumberFormatException–数字格式异常
  • SecurityException–安全异常
  • UnsupportedOperationException–不支持的操作异常

    try-catch语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    try{
    //打开文件
    //判断大小
    //分配内存
    //读入内存
    //关掉文件
    }catch(/*文件打开失败*/){
    //处理代码
    }catch(/*大小取值失败*/){
    //处理代码
    }catch(/*内存分配失败*/){
    //处理代码
    }catch(/*读取失败*/){
    //处理代码
    }catch(/*关闭文件失败*/){
    //处理代码
       }finally{
       //总是执行的代码,即使碰到return也执行finally后才return,除非遇到System.exit(),程序会立刻退出
    }

throw/throws抛出异常

1
2
3
4
5
6
7
8
//抛出的对象必须是Throwable的子类
public int read () throws IOException{
......
}
public static void main(String args[]) throws IOException, IndexOutOfBoundsException {
               ······
}

堆栈调用机制

  • 如果一个try-catch块中没有处理,那么将会抛向此方法的调用者
  • 如果一个异常回到main方法,而且也没有处理,那么程序终将终止
    堆栈处理机制

    自定义异常类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class MydateException extends Exception{
    private String reason;
    public MydateException(String r){
    reason = r;
    }
    public String getReason(){
    return reason;
    }
    }
    public class Mydate {
    int year,month,day;
    void setDate(int year,int month,int day) throws MydateException{
    if(day>31)
    throw new MydateException("day too big");
    this.year = year;
    this.month = month;
    this.day = day;
    }
    public static void main(String[] args){
    Mydate t = new Mydate();
    try {
    t.setDate(2001,1,100);
    }catch (MydateException e){
    System.out.println(e.getReason());
    }
    }
    }

问答题 :

问:请简单描述下面方法的执行流程和最终返回值是多少?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static int test1(){
int ret = 0;
try {
return ret;
}finally {
ret = 2;
}
}
public static int test2(){
int ret = 0;
try {
int a = 5 / 0 ;
return ret;
}finally {
return 2;
}
}
public static int test3(){
try {
int a = 5 / 0;
}finally {
throw new RuntimeException("hello");
}
}

答:本题旨在考察 try-catch-finally 块的用法踩坑经验,具体解析如下。

test1 方法运行返回 0,因为执行到 try 的 return ret; 语句前会先将返回值 ret 保存在一个临时变量中,然后才执行 finally 语句,最后 try 再返回那个临时变量,finally 中对 ret 的修改不会被返回。

test2 方法运行返回 2,因为 5/0 会触发 ArithmeticException 异常,但是 finally 中有 return 语句,finally 中 return 不仅会覆盖 try 和 catch 内的返回值且还会掩盖 try 和 catch 内的异常,就像异常没有发生一样(特别注意,当 finally 中没有 return 时该方法运行会抛出 ArithmeticException 异常),所以这个方法就会返回 2,而且不再向上传递异常了。

test3 方法运行抛出 hello 异常,因为如果 finally 中抛出了异常,则原异常就会被掩盖。

因此为避免代码逻辑混淆,我们应该避免在 finally 中使用 return 语句或者抛出异常,如果调用的其他代码可能抛出异常,则应该捕获异常并进行处理。

问:如果执行 finally 代码块之前方法返回了结果或者 JVM 退出了,这时 finally 块中的代码还会执行吗?

答:只有在 try 里面通过 System.exit(0) 来退出 JVM 的情况下 finally 块中的代码才不会执行,其他 return 等情况都会调用,所以在不终止 JVM 的情况下 finally 中的代码一定会执行。

问:java 中 finally 块一定会执行吗?

答:不一定,分情况。因为首先想要执行 finally 块的前提是必须执行到了 try 块,当在 try 块或者 catch 块中有 System.exit(0); 这样的语句存在时 finally 块就不会被执行到了,因为程序被结束了。此外当在 try 块或者 catch 块里 return 时 finally 会被执行;而且 finally 块里 return 语句会把 try 块或者 catch 块里的 return 语句效果给覆盖掉且吞掉了异常。

Tags: Java
使用微信添加

若你觉得我的文章对你有帮助,请添加我为好友

扫描二维码,分享此文章