java异常处理

目录

一、什么是异常

二、异常处理

        1、如何捕捉异常?

        2、finally语句

        3、finally语句中的throw和return 

三、常见异常

四、自定义异常

        那么具体如何自定义异常类呢?

        throw关键字

        throws关键字

五、运行时异常

六、异常处理流程 

‍️‍️多多支持




前言:

        在程序设计和运行的过程中,发生错误是不可避免的。为此java提供了异常的处理机制,来帮助程序员检查可能出现的错误。保证程序的可读性和可维护性。java将异常封装到一个类中,出现错误时就会抛出异常,程序终止。

 通过学习,您可以了解到:

1、什么是异常
2、如何捕捉异常
3、java中有哪些常见的异常
4、如何自定义异常
5、如何在方法中抛出异常
6、运行时有哪些异常
7、异常处理的使用原则




一、什么是异常

        错误产生于没有实现预料到的各种情况,或是超过了程序员可控制范围的环境因素。
就比如我们经常遇到的遇到除数为0的情况,但是自身又没发现,于是程序运行到这一样是就会让程序异常终止:
java异常处理_第1张图片

① 算术异常 

        程序运行时发生了算数异常ArithmeticException,根据异常提示可以知道是在Test2的main方法中发生了算术异常,异常为 / by zero,意为0被作为了除数而报错。
java异常处理_第2张图片

         可以看到,异常前的程序都会正常执行,当执行到异常,系统就不会再执行下去,提前结束并提示:程序异常退出

Process  finished  with  exti  code  1

异常类为ArithmeticException

        还有很多例子,例如

②数组越界:

        代码案例: 

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{0,1,2,3,4,5};
        System.out.println(arr[6]);
    }
}

         运行结果:

 异常类为ArrayIndexOutOfBoundsException

③空引用的使用空指针):

        代码案例:

public class Main {
    public static void main(String[] args) {
        String tem = null;
        int stringLength = tem.length();
        System.out.println(stringLength);
    }
}

        执行结果:

java异常处理_第3张图片

 异常类为NullPointerException

        java是面向对象的计算机语言,所以在java中异常都是作为类的实例的形式出现的。当某一方法中发生错误时,这个方法会创建一个对象,并且把它传递给正在运行的系统。   这个对象就是异常对象,通过异常处理机制,可以将非正常情况下的处理代码与程序的主逻辑分离,即在编写代码主流程时在其他地方处理异常。

        这些异常种类和异常处理机制将会在下面的篇章中逐渐渗透。




二、异常处理

        为了保证程序能正常的运行,我们需要对出现的异常错误进行及时的处理。在java 的异常处理机制当中,当某个方法出现异常时,可以在该方法里进行捕捉,然后处理异常,也可以将异常向上抛出,交给该方法的调用者处理。

        如果对产生的异常不做任何的处理,程序就会异常终止。所以需要对该异常进行捕捉然后处理。

        1、如何捕捉异常?

        java语言的异常捕捉结构为:

try{

        // 可能产生异常错误的语句

}catchExceptionTypeOne  e){

        // 对异常 ExceptionTypeOne 的处理语句

}catch ( ExceptionTypeTwo e) {

        // 对ExceptionTypeTwo 的处理语句

}

...........

finally {

        // 语句块

}

此结构由 trycatch finally  3部分组成。其中:
①  try语句中的是可能发生异常的代码
②  catch用来捕捉异常,{}中为异常处理的语句
③  finally为异常处理的最后执行部分,无论前面的catch是否被捕捉,finally语句都会被执行

其中的  Exception 是try代码块传递给catch代码块的变量类型,e是变量名。

         我们使用这种try -  catch语句来对上面的 / by zero异常来进行一个修改:

public class Test2 {
    public static void main(String[] args) {
        try {
            int digit = 1;
            int zero = 0;
            System.out.println("value of digit is:"+digit);
            System.out.println("value of zero is:"+zero);
            int result = digit / zero;
            System.out.println("value of result is:" + result);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("hello world!");
    }
}

        catch代码块中的语句e.printStackTrace() 语句时用于输出错误性质,通常,异常处理用下面三个函数来获取异常有关信息:

getMessage()   // 输出错误性质

toString() // 给出一场的性质和类型

printStackTrace() // 指出异常类型、性质、栈层次及出现在程序的位置

运行结果如图:
java异常处理_第4张图片

        可以发现,digit 和 zero 的值都被打印,但是result的值没有被打印,try - catch 语句外的打印
hello world 的语句也被打印。紧接着的一行就是  Process finished  with  exit  code  0 
表示程序正常退出。

        当有多个catch 语句的情况:



public class Main {
    private static int Test(int x, int y) throws MyException{
        if(y < 0){
            throw new MyException("除数不能为负数");
        }
        return x/y;
    }
    public static void main(String[] args) {
        try {
            int ret = Test(2,-1);
        } catch (MyException e) {
            System.out.println(e.getMessage());  // 进行捕捉
        } catch (ArithmeticException e){
            System.out.println(e.getMessage());
        } catch (Exception e){
            System.out.println("发生了其他异常");
        }
    }
}

        上面实例使用了很多个catch语句来捕捉异常, 其中

① 调用Test (2 , -1 ) 将会发生MyException异常,进而转入catch (MyException e)执行

② 调用Test (3 , 0) 将会发生ArithmeticException异常,转入对应的catch语句执行

③  如果还有除了 0 和 负数之外的异常,那么将会被Exception e捕捉然后转入catch语句执行

但是由于  Exception是所有异常类的父类,如果将其与MyException调换位置如果图:
java异常处理_第5张图片

 那么 前面的 ArithmeticException异常和 MyException 异常都会被 第一个catch语句捕捉,
那么后面的catch语句就不会执行,最后就只会收到一个发生了其他异常的字符串提示,对于异常的处理没有任何的帮助,try - catch语句也就是去了它的意义。

        通过try - catch 语句,程序没有异常退出,而是将错误抛出后,继续执行try  -  catch 异常捕捉后面的语句,而不至于因为一个异常从而影响整个程序的运行

        2、finally语句

        一个完整的try  -  catch 语句一定要包含finally语句无论程序是否有异常发生,并且无论之间的try -  catch语句是否顺利执行完毕,都会执行finally语句
        但是也存在意外,finally语句不会被执行的情况,如下:

① 在finally 语句中发生了异常

② 在前面的代码中使用了System.exit()语句,程序提前结束

③ 程序所有的线程死亡

④ 关闭CPU

        3、finally语句中的throw和return 

无论有没有异常都会执行finally语句,那么如果在try-catch语句块中遇到了return和throw语句,后面的finally语句是否还会执行呢?代码案例如下:

public class Test { 
    public static void main(String[] args) { 
        System.out.println("return value of getValue(): " +
        getValue()); 
    } 
     public static int getValue() { 
         try { 
             return 0; 
         } finally { 
             return 1; 
         } 
     } 
 }

 程序的运行结果是什么?

解析:当Java程序执行ty块、catch块时遇到了retun语句, 并不会立马结束这个方法,而是去寻找该异常处理流程中是否包含finally语句块,如果没有finally块,方法终止,返回相应的返回值。如果有finally块, 系统立即开始执行finally块中的语句,只有当finally块执行完成后,系统才会再次回到try-catch语句中来根据return语句结束方法。如果finally块里使用了return语句,则finally块会直接执行return语句并返回,使方法结束,系统将不会跳回去执行try块、 catch块里的任何代码。

结果如下:

 return value of getValue(): 1




三、常见异常

        java内部提供了一些异常,用来描述经常发生的错误,其中,有的需要程序员极性捕捉处理或声明抛出。有的是由java虚拟机自动进行捕捉

        java常见的异常类如表:

java常见的异常类
异常类 说明
ClassCastException 类型转换异常
ClassNotFoundException 未找到相应的类异常
ArithmeticException 算术异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ArrayStoreException 数组中包含不兼容的值抛出的异常
SQLException 操作数据库异常类
NullPointerException 空指针异常
NoSuchFieldException 未找到字段异常
NoSuchMethodException 方法未找到抛出的异常
NumberFormatException 字符串转化为数字抛出的类异常
NegativeArraySizeException 数组元素个数为负数抛出的异常
StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
IOException 输入输出异常
IllegalAccessException 不允许访问某类异常
InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常
EOFException 文件已结束异常
FileNotFoundException 文件未找到异常

         以上异常的出现场景希望读者可以自行研究。




四、自定义异常

        使用java内置的异常类可以处理在编程期间出现的大多数异常情况,但是仍有部分比较有特性的异常是java内置异常没有覆盖到的。例如:对于需要传入整数的一个方法,你可能会觉得,传入 一个负数是不符合你的期望,认为负数作为这个方法的参数为一个错误的值,但是对于java来说,它不会将其视为一个错误或者异常。

        所以为了能个性化地去解决这些在特殊情况才能出现的异常,java引入了自定义异常类,用户只需要继承Exception类即可以自定义异常类。

        那么具体如何自定义异常类呢?

① 首先,在java中异常都是作为类的实例的形式出现的,所以我们应该先定义自定义的类,类名设为MyException(自定义类名视使用场景况而定),然后由该类继承Exception类:

        代码案例:

public class MyException extends Exception{    // 声明这个MyException类并让其继承Exception类
    public MyException (String ErrorMessage){    // 调用构造方法
        super(ErrorMessage);   // 给父类Exception传参
    }
}

        字符串ErrorMessage是要输出的错误信息。

使用throw关键字,在需要的时候抛出异常,就比如我们经常使用ArrayList表里面的set方法:
java异常处理_第6张图片

 里面就是使用了rangeCheck()方法来检查index是否越界,点击查看rangeCheck方法可以看到如下:
java异常处理_第7张图片

 在这里通过使用throw 关键字抛出对应的异常信息,里面的outOfBoundMsg(index)返回一个错误 的异常信息的字符串,对应我们自定义异常的  ErrorMessage  :
java异常处理_第8张图片

         然后使用构造方法构造new 出一个IndexOutOfBoundsException 的实例并抛出异常。

        我们使用自定义异常配合try- catch语句来模拟这个过程:
 



public class Main {
    private static void checkMySet(int pos,int len) throws MyException{
        if(pos >= len){
            throw new MyException("指定的下标超过已有的最大下标位置");
        } else if (pos < 0 ) {
            throw new MyException("指定下标小于0!");
        }else {
            System.out.println("下标数值正确!");
        }
    }
    public static void set(int[] arr,int pos,int element) throws MyException {
        checkMySet(pos,arr.length);
        arr[pos] = element;
    }
    public static void main(String[] args) {
        int[] arr = new int[10];
        try {
            set(arr,-4,10);   // 可能抛出异常
        } catch (MyException e) { 
            System.out.println(e);  // 进行捕捉
        }
    }
}

结果为:

java异常处理_第9张图片

程序正常退出。 

        throw关键字

        throw关键字通常用于方法体重,并且抛出一个异常的实例化对象,对象被抛出后程序终止:

java异常处理_第10张图片

 可以看到,如果在方法体内抛出对象的语句后面加上语句,那么后面的语句将永远不会执行到

代码案例如下:

public class Main {
    private static void Test(int pos) throws MyException{
        if(pos >= 0){
            throw new MyException("pos >= 0");
        }
        System.out.println("checkEnded");
    }
    public static void main(String[] args) {
        int[] arr = new int[10];
        try {
            System.out.println("TestStart");
            Test(0);
            System.out.println("TestEnd");
        } catch (MyException e) {
            System.out.println(e);  // 进行捕捉
        }
    }
}

 执行结果:
java异常处理_第11张图片

 可以看出来,在方法抛出异常后,该方法后面的语句也不会执行

如果要捕捉异常,就必须使用try - catch语句

        此外,throw必须写在方法体内部 ,抛出的对象必须是Exception 或者 Exception 的子类对象。如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理  如果抛出的是编译时异常,用户必须处理,否则无法通过编译 。异常一旦抛出,其后的代码就不会执行

        throws关键字

        throws关键字通常被用来在声明方法的时候,指定其方法可能抛出的异常,多个异常可以用“,”逗号分割:

public static void Test ()  throws  Exception1 , Exception2 , Exception3 {

        //可能会抛出Exception1 , Exception2 , Exception3异常的方法体

}

        从上面的代码案例中我们可以看到:checkMySet()方法中加入了if 语句来来判断是否抛出异常,有可能会抛出,也可能不会抛出,只要有抛出的可能,就需要在其方法后用throws关键字声明可能会抛出的异常类。

        处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws 将异常抛 给方法的调用者来处理。

        异常被throws 不断向上抛出,最终被catch 捕获并处理。

需要注意的是: 

throws必须跟在方法的参数列表之后
② 声明的异常必须是 Exception 或者
Exception 的子类




五、运行时异常

        RuntimeException 异常是程序运行过程中产生的异常。它是Exception类的子类之一。
java异常处理_第12张图片

         java类库的每个包中都定义了异常类,所有的类都是Throwable的子类,Throwable派生出两个子类,分别是Exception 和 Error 类。 Error 类及其子类用来描述java运行系统中的内部错误以及资源消耗的错误。

        Exception类异常通过捕捉后程序可以继续正常执行。

 java异常处理_第13张图片

 其中RuntimeException的异常种类

         从图中可以看出来,Throwable 是异常处理体系的顶层类,派生除了Error 和 Exception 两个重要的子类。Error 指的是java虚拟机无法解决的严重问题,例如jvm内部错误,资源耗尽等等,其代表为: StackOverflowError  和 OutOfMemoryError

        Exception 异常类为程序员可以正常捕捉并处理的异常。

RuntimeException异常
NullPointerException 空指针异常
ArrayIndeOutOfBoundsException 数组下标越界异常
ArithmeticException 算术异常
SecurityException 安全性异常
NegativeArrayException 数组长度为负异常
IllegalArgumentException 非法参数异常
ArrayStoreException 数组中包含不兼容参数异常




六、异常处理流程 

        程序先执行 try 中的代码 如果 try 中的代码出现异常 , 就会结束 try 中的代码 , 看和 catch 中的异常类型是否匹配 . 如果找到匹配的异常类型, 就会执行 catch 中的代码 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者 . 无论是否找到匹配的异常类型, fifinally 中的代码都会被执行到 ( 在该方法结束之前执行 ). 如果上层调用者也没有处理的了异常, 就继续向上传递 . 一直到 main 方法也没有合适的代码处理异常 , 就会交给 JVM 来进行处理 , 此时程序就会异常终止。
        
        java异常强制用户去考虑程序的强健性和安全性。当程序运行到某个方法可能会出现异常时,可以在当前方法中使用try - catch 语句捕获异常。
        如果一个方法被覆盖的时候,则需要在他的上级方法中声明抛出相同异常或异常的子类。如果父类中抛出多个异常,则覆盖方法必须抛出那些异常的的一个子集,不能抛出新的异常。

你可能感兴趣的:(javaSE,jvm,青少年编程,开发语言)