Java异常

原文链接:Java 里的异常(Exception)详解

(一)什么是java里的异常

由于java是c\c++ 发展而来的,  首先我们先看看c语言里的错误.

1.c语言里的错误

我们实现一个程序的过程包括,  代码编写, 编译代码成为程序,  执行程序.其中大部分常见的语法错误都会被编译代码这样部过滤掉.   但是即使通过了编译. 执行程序这一步可能还是会有错误.

原因很多, 例如常见的除数为0,    内存溢出(数组的index超出界限), 或者内存被其他程序修改等.

#include <stdio.h>  
  
int f(int a, int b){  
    return a/b;  
}     
  
int main(){  
    int i = f(8,0);  
        printf("i is %d\n",i);  
    return 0;     
}  

c语言里对这种执行时出现的错误是无能为力的,  一旦出错就会整个程序崩溃, 就不会在继续执行下面的代码.而且很多时候出错信息很少, 让你无法判断出错的原因和地方, 只能一步步小心debug...

所以很多用c写的程序有时会出现非法关闭的现象.解决方法只能是在代码里对可能出错的地方添加if 判断.

2.java里运行时出现的错误

java里编译器对代码的规范性比c严格得多. 但是即使如此,  通过编译的java程序有时也很难避免执行时出错.

package Exception_kng;  
  
class Exp1{  
    public int f(int a, int b){  
        return a/b;  
    }  
}  
  
public class Expt_1{  
    public static void g(){  
        Exp1 e = new Exp1();  
        int i = e.f(8,0);  
        System.out.printf("i is %d\n", i);  
    }  
}  

jvm虚拟机是会对错误作出一定的处理的.所以可以简单地将java里的异常理解成java运行时出现的错误,  异常机制就是对这种错误进行处理的机制.

3.java异常的定义

异常是指程序在运行过程中发生的,由于外部问题导致的程序运行异常事件,异常的发生往往会中断程序的运行。在 Java 这种面向对象的编程语言中,万物都是对象,异常本身也是一个对象,程序发生异常就会产生一个异常对象。

  • 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
  • 异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
  • 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。
  • Java为异常设计了一套异常处理机制,当程序运行过程中发生一些异常情况时,程序不会返回任何值,而是抛出封装了错误信息的异常对象。这样保证程序代码更加优雅,并提高程序的健壮性。 为什么要设计异常呢?首先,引入异常之后,我们就可以把错误的代码从正常代码中分离出来进行单独处理,这样使代码变得更加整洁;其次,当出现一些特殊情况时,我们还可以抛出一个检查异常,告知调用者让其处理。

可能出现的异常的代码并不是肯定会出现异常, 取决于执行环境和数据.!

(二)java里的异常的分类

                       Throwable
                      /                \
              Error             Exception
                 /                   /               \
         xxxxxx             xxxxxx          RuntimeException
                                                          /                   \

                                                     xxxxxx             ArithmeticException

上图的所有对象都是类.     
  1. Throwable:代表是可抛出的.
  2. Error:代表的是严重错误,  这种错误程序员无法进行处理, 例如操作系统崩溃, jvm出错, 动态链接库失败等.  Error并不是异常, 不是本文的重点.
  3. Exception:代表的就是异常了.  它下面很多派生类,   其中它的派生类也分两种, 一种是RuntimeException(运行时异常), 其他的都是非运行时异常
  4. RuntimeException:包括除数为0, 数组下标超界等. 运行时异常的派生类有很多, 其产生频率较高.  它的派生类可以由程序处理或者抛给(throw) 给jvm处理. 例如上面的例子就是抛给了jvm处理, jvm把程序中断执行, 并把错误信息输出到终端上.
  5. 非RuntimeExcption:这种异常属于Excepion的派生类(上面红色的xxx), 但是不是RuntimeException的派生类,  这种异常必须由程序员手动处理,否则不通过编译.
  6. ArithmeticExcpetion:算术异常, 它是RuntimeException的派生类, 所以程序员不手动处理也通过编译, 只不过出错时会被jvm处理.

Exception:分为未检查异常(RuntimeException)已检查异常(非RuntimeException)

未检查异常是因为程序员没有进行必需要的检查,因为疏忽和错误而引起的错误。几个经典的RunTimeException如下:

1.java.lang.NullPointerException;
2.java.lang.ArithmaticException;
3.java.lang.ArrayIndexoutofBoundsException

Exception 属于应用程序级别的异常,这类异常必须捕捉,Exception体系包括RuntimeException体系和其他非RuntimeException的体系

RuntimeException 表示系统异常,比较严重,如果出现RuntimeException,那么一定是程序员的错误

(三)java里对异常的处理

java里对异常的处理有三种.

1.使用try catch处理

package Exception_kng;  
  
class Exp2{  
    public int f(int a, int b){  
        int i = 0;  
        try{  
            i = a/b;  
        }  
        catch(Exception e){  
            System.out.printf("Exception occurs!!\n");  
            System.out.println(e.getMessage());  //print the root cause  
            System.out.printf("===========================\n");  
            e.printStackTrace(); //print the info of function stuck.  
        }  
  
        return i;  
    }  
}  
  
public class Expt_2{  
    public static void g(){  
        Exp2 ex = new Exp2();  
        int i = ex.f(8,0); //call f()  
        System.out.printf("i is %d\n", i);  //successfully executed  
    }  
}  

在f()函数中对可能出现的异常的代码进行try catch处理后,  程序会执行catch里的代码. 而且不会中断整个程序, 继续执行try catch后面的代码.

也就是说try catch处理后并不会终止程序, 程序即使出现了错误,  也可以对错误进行一定的处理后继续执行. 这就是java异常机制比c语言安全的地方.

  • 注:
  • getMessage() 方法:   Exception类的方法之一,  返回异常的原因,   上面的 / by zero 就是这个方法输出的.
  • printStackTrace():      Exception类的方法之一,  在屏幕输出函数栈信息,  也就是异常出现的地方.

2.使用throw or throws 关键字

函数里并不处理异常, 使用throw or throws 关键字 把可能出现的异常抛给调用该函数的上级函数处理.

例如我在f()函数中不想处理可能出现的异常,  想把它抛出上级函数处理:

package Exception_kng;  
  
class Exp3{  
    public int f(int a, int b){  
        if (0 == b){  
            throw new ArithmeticException("Shit !!! / by zero!");  
              
        }   
  
        return a/b;  
    }  
}  
  
public class Expt_3{  
    public static void g() throws ArithmeticException{  
        Exp3 ex = new Exp3();  
        int i = 22;  
        i = ex.f(8,0); //throw excetpion   
        System.out.printf("i is %d\n", i);  //failed executed   
        System.out.printf("g() is done!!\n");  //failed executed  
    }  
  
    public static void h(){  
        try{  
            g();      
        }catch(ArithmeticException e){  
            System.out.printf("Exception occurs!!\n");  
            System.out.println(e.getMessage());  //print the root cause  
            System.out.printf("===========================\n");  
            e.printStackTrace(); //print the info of function stuck.  
        }  
  
        System.out.printf("h() is done!!\n");  //successfully executed  
    }  
}  

可以见到f() 加了个条件判断, 如果参数b = 0, 使用throw 直接手动抛出1个异常. 让调用它的函数处理.

g()调用f()函数, 预见到f()可能有异常, 但是也不想处理,  使用throws 关键字告诉调用它的函数本函数有可能抛出这种异常. // 注, 这里的throws对程序并没有实质的影响.

h()调用g(),  简单g()定义的throws, 用try catch在本函数进行处理.

注意这个程序没有执行g() 最后的代码.

3.交给jvm虚拟机处理

假如上面的例子h() 也不处理怎么办?  就如(一)中 2 的例子, 会抛给jvm处理.

package Exception_kng;  
  
class Exp1{  
    public int f(int a, int b){  
        return a/b;  
    }  
}  
  
public class Expt_1{  
    public static void g(){  
        Exp1 e = new Exp1();  
        int i = e.f(8,0);  
        System.out.printf("i is %d\n", i);  
    }  
}  

但是这种情况只适用于RuntimeExecption及其派生类.

jvm怎么处理呢,  就是中断整个程序, 并把异常信息输出到屏幕上.

  1. 实际上, 当java程序的一个函数抛出异常时,  
  2. 首先会检查当前函数有没有try catch处理, 如果无检查上一级函数有无try..catch处理....
  3. 这样在函数栈里一级一级向上检查, 如果直至main函数都无try..catch, 则抛给jvm..

项目中强烈建议尽量手动处理, 不要把异常交给jvm.

 

posted @ 2023-05-02 16:01  ImreW  阅读(11)  评论(0编辑  收藏  举报