java异常处理

目录🚀🚀🚀

一、什么是异常

二、异常处理

        1、如何捕捉异常?

        2、finally语句

        3、finally语句中的throw和return 

三、常见异常

四、自定义异常

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

        throw关键字

        throws关键字

五、运行时异常

六、异常处理流程 

😶‍🌫️😶‍🌫️多多支持🦆




前言:

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

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

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




一、什么是异常

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

① 算术异常 

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

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

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);
    }
}

        执行结果:

 异常类为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() // 指出异常类型、性质、栈层次及出现在程序的位置

运行结果如图:

        可以发现,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调换位置如果图:

 那么 前面的 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方法:

 里面就是使用了rangeCheck()方法来检查index是否越界,点击查看rangeCheck方法可以看到如下:

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

         然后使用构造方法构造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);  // 进行捕捉
        }
    }
}

结果为:

程序正常退出。 

        throw关键字

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

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

代码案例如下:

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);  // 进行捕捉
        }
    }
}

 执行结果:

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

如果要捕捉异常,就必须使用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类库的每个包中都定义了异常类,所有的类都是Throwable的子类,Throwable派生出两个子类,分别是Exception 和 Error 类。 Error 类及其子类用来描述java运行系统中的内部错误以及资源消耗的错误。

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

 

 其中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 语句捕获异常。
        如果一个方法被覆盖的时候,则需要在他的上级方法中声明抛出相同异常或异常的子类。如果父类中抛出多个异常,则覆盖方法必须抛出那些异常的的一个子集,不能抛出新的异常。
  • 20
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值