Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 8491|回复: 0

Java异常体系和异常处理机制

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-5 15:32:05 | 显示全部楼层 |阅读模式

    异常简介

    在程序运行过程中出现错误,导致程序出现非预期场景。异常处理可以保证出现错误后,控制接下来的程序流程,是选择定位错误信息,还是抛出异常或捕获异常、还是避免程序非正常退出,都取决于我们。

    Java的异常体系结构(来自网络)

       

    Java把异常作为一种类,当做对象来处理。所有异常类的基类是Throwable类,两大子类分别是Error和Exception。这些异常类可以分为三种类型:系统错误、异常和运行时异常。系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。

    异常(Exception)又分为RuntimeException(运行时异常)和CheckedException(检查异常),两者区别如下:

    • RuntimeException:程序运行过程中出现错误,才会被检查的异常。例如:类型错误转换,数组下标访问越界,空指针异常、找不到指定类等等。
    • CheckedException:来自于Exception且非运行时异常都是检查异常,编译器会强制检查并通过try-catch块来对其捕获,或者在方法头声明该异常,交给调用者处理。

    两种异常的处理方式:若是运行时异常,则表明程序出错,应该找到错误并修改,而不是对其捕获。若是检查异常,遵循该原则:谁知情谁处理,谁负责谁处理,谁导致谁处理。处理就是对其捕获并处理。

     

    深入了解异常处理

    异常处理的5个关键字:try、catch、throw、throws和finally关于它们的用法和注意点,会在下面一一介绍。

    Java的异常处理模型基于三种操作: 声明异常、抛出一个异常和捕获一个异常。

    声明异常(throws)

                                                                //不捕获,而是声明该异常,交给调用者处理
       public static void method() {                           public static void method2() throws Exception {
           /*try-catch块捕获异常*/                                     if(5 > 3) {            
           if(5 > 3) {                                                   throw new Exception(); //抛出异常        
               try {                                                   }        
                   throw new Exception(); //抛出异常             }
               } catch (Exception e) {
                   e.printStackTrace();//捕获异常后的处理
               }
           }
       }

    在Java中,当前执行的语句必属于某个方法,Java解释器调用main方法执行开始执行程序。若方法中存在检查异常,如果不对其捕获,那必须在方法头中显式声明该异常,以便于告知方法调用者此方法有异常,需要进行处理。 在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。如下所示:

        public static void method() throws IOException, FileNotFoundException{
            //something statements
        }

    【注意】若是父类的方法没有声明异常,则子类继承方法后,也不能声明异常。

     

    抛出异常(throw)

    如果代码可能会引发某种错误,可以创建一个合适的异常类实例并抛出它,这就是抛出异常。如下所示:

        public static double method(int value) {
            if(value == 0) {
                throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常
            }
            return 5.0 / value;
        }

    大部分情况下都不需要手动抛出异常,因为Java的大部分方法要么已经处理异常,要么已声明异常。所以一般都是捕获异常或者再往上抛。

    捕获异常(try-catch)

    当抛出一个异常时,可以在try-catch块中捕获它并进行处理。

            try {
                //包含可能会出现异常的代码以及声明异常的方法
            } catch (ClassCastException e) {
                //捕获指定异常并进行处理
            }catch(Exception ex) {
                //捕获指定异常并进行处理
            }

    若执行try块的过程中没有发生异常,则跳过catch子句。若是出现异常,try块中剩余语句不再执行。开始逐步检查catch块,判断catch块的异常类实例是否是捕获的异常类型。匹配后执行相应的catch块中的代码。如果异常没有在当前的方法中被捕获,就会被传递给该方法的调用者。这个过程一直重复,直到异常被捕获或被传给main方法(交给JVM来捕获)。

    catch捕获异常的顺序

    一个通用父类可以派生出各种异常类,如果一个catch块可以捕获一个父类的异常对象,它就能捕获那个父类的所有子类的异常对象。如果捕获的是多个同类型异常,则子类异常在前,父类异常在后,不然会导致编译错误。这是因为父类异常囊括了子类异常,如果父类异常在前,子类异常永远捕获不到,导致有时候无法准确描述错误信息。

            try {
                File f =new File("C:\\ProgramFile\\test.txt");
                FileInputStream fis = new FileInputStream(f);
            } catch (FileNotFoundException e) { //子类异常
                e.printStackTrace();
            } catch(IOException ie) {  //父类异常
                ie.printStackTrace();
            } catch(Exception e) { //基类运行时异常
                e.printStackTrace();
            }

    这里只是用于演示catch块捕获的顺序。捕获多个异常时,可以使用catch(Exception1 | Exception2| Exception3)的形式来优化捕获异常的代码结构。

     

    将声明异常、抛出异常和捕获异常综合在一起。演示如下:

        public static void main(String[] args) {
            for(int i = 2; i < 100; i++) {
                //对运行时异常,可以选择捕获也可以不选择捕获
                if(isPrime(i)) {
                    System.out.print(i + " ");
                }
            }
        }        //检测是否为质数
        public static boolean isPrime(int num) throws IllegalArgumentException{
            //抛出一个运行时异常
            if(num < 2) throw new IllegalArgumentException("质数不能小于2");
            for(int i = 2; i < num; i++) {
                if(num % i == 0) {//若能被1和本身之外的数整除,则非质数
                    return false;
                }
            }
            return true;
        }

    因为抛出的是运行时异常,可以选择捕获或者不捕获。但如果抛出检查异常,在编译时就必须选择捕获或者声明。

     

    finally语句块

    无论是否有异常,finally块中的代码总是会被执行的。 finally语句在执行关闭资源的语句时非常有用。

            //第一种形式                                                      //第二种形式                            
            try {                                                            try {
                //执行程序代码,可能会出现异常                                     //执行程序代码,可能会出现异常                                        
            }catch(Exception e) {                                            }finally {    
                //捕获异常并处理                                                 //必执行的代码    
            }finally {                                                       }
                //必执行的代码
            }

    try-catch-finally的执行流程

    try块中引起异常,异常代码之后的语句不再执行,若被catch块捕获,执行匹配的catch块,然后执行finally语句。若catch块不能捕获异常,则执行finally语句,之后将异常传递给这个方法的调用者。

            Scanner sc = new Scanner(System.in);
            int a = 0; //保证局部变量a在各个块中可用
            try {
                a = sc.nextInt();
                if(a < 0) throw new IllegalArgumentException();
                System.out.println("执行完try语句。a:" + a);
            }catch(IllegalArgumentException e){
                System.out.println("执行catch语句");
                System.out.println("数值小于0,不符合。已设为默认值 1");
                a = 1;
            }finally {
                System.out.println("执行finally语句。a:" + a);
            }
        //未引发异常                                      //引发异常并捕获
        5                                                -5
        执行完try语句。a:5                                执行catch语句
        执行finally语句。a:5                              数值小于0,不符合。已设为默认值 1
                                                        执行finally语句。a:1

    try-finally的执行流程

    try块中引起异常,异常代码之后的语句不再执行,直接执行finally语句。 try块没有引发异常,则执行完try块就执行finally语句。

    try-finally可用在不需要捕获异常的代码,可以保证资源在使用后被关闭。例如IO流中执行完相应操作后,关闭相应资源;使用Lock对象保证线程同步,通过finally可以保证锁会被释放;数据库连接代码时,关闭连接操作等等。

            //以Lock加锁为例,演示try-finally
         ReentrantLock lock = new ReentrantLock(); try { //需要加锁的代码 }finally { lock.unlock(); //保证锁一定被释放 }
    finally遇见如下情况不会执行
    • 在前面的代码中用了System.exit()退出程序。
    • finally语句块中发生了异常。
    • 程序所在的线程死亡。
    • 关闭CPU。

     

    try、catch、finally、throw和throws使用归纳

    • try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。
    • try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理。
    • finally语句块中的代码一定会被执行,常用于回收资源 。
    • throws:声明一个异常,告知方法调用者。
    • throw :抛出一个异常,至于该异常被捕获还是继续抛出都与它无关。
     
    Java编程思想一书中,对异常的总结。
    1. 在恰当的级别处理问题。(在知道该如何处理的情况下了捕获异常。)
    2. 解决问题并且重新调用产生异常的方法。
    3. 进行少许修补,然后绕过异常发生的地方继续执行。
    4. 用别的数据进行计算,以代替方法预计会返回的值。
    5. 把当前运行环境下能做的事尽量做完,然后把相同的异常重抛到更高层。
    6. 把当前运行环境下能做的事尽量做完,然后把不同的异常抛到更高层。
    7. 终止程序。
    8. 进行简化(如果你的异常模式使问题变得太复杂,那么用起来会非常痛苦)。
    9. 让类库和程序更安全。

     

    自定义异常

    通过继承Exception类来定义一个异常类。Java已经提供了很多异常类,尽量使用这些异常类而不要创建自己的异常类。除非Java的异常类不能很好地描述问题时,才自定义异常来进行准确描述。对于自定义异常,不需要太多功能,类名能准确描述问题是关键。

    自定义异常如下所示:

    //判断长度是否合法的自定义异常
    public class LengthException extends Exception {
        public LengthException() {}
        public LengthException(String s) {
            super(s);
        }
        @Override
        public String getMessage() {
            return super.getMessage();
        }
    }

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 19:06 , Processed in 0.064196 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表