写点什么

【Java 基础你一定要掌握的知识点】Java 异常处理和设计

作者:猫的树
  • 2022 年 7 月 13 日
  • 本文字数:4046 字

    阅读完需:约 13 分钟

【Java 基础你一定要掌握的知识点】Java异常处理和设计

异常(exception)是在运行程序时产生的一种异常情况,已经成为了衡量一门语言是否成熟的标准之一。

一、什么是异常

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。


那如果程序产生异常,是任程序自生自灭,立刻退出终止,还是输出错误给用户?Java 提供了更加优秀的解决办法:异常处理机制


Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。

二、异常的分类

为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类 Throwable 类。


Error 和 Exception

Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。


Error 是无法处理的异常,比如 OutOfMemoryError,一般发生这种异常,JVM 会选择终止程序。因此我们编写程序时不需要关心这类异常。


其中异常类 Exception 又分为运行时异常非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

常见的 Error 和 Exception

1)运行时异常(RuntimeException):


• NullPointerException:空指针异常;• ClassCastException:类型强制转换异常• IllegalArgumentException:传递非法参数异常• IndexOutOfBoundsException:下标越界异常• NumberFormatException:数字格式异常


2)非运行时异常:


• ClassNotFoundException:找不到指定 class 的异常• IOException:IO 操作异常


3)错误(Error):


• NoClassDefFoundError:找不到 class 定义异常• StackOverflowError:深递归导致栈被耗尽而抛出的异常• OutOfMemoryError:内存溢出异常

异常方法

介绍 Throwable 类的主要方法:


三、Java 异常处理机制

Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。


try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。


代码说明:


try {    逻辑程序块} catch(ExceptionType1 e) {    处理代码块1} catch (ExceptionType2 e) {    处理代码块2    throw(e);    // 再抛出这个"异常"} finally {    释放资源代码块}
复制代码


异常处理的机制如下:• 在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。• 对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。


再来看下面一段代码:


import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;  public class Main {    public static void main(String[] args) {        String str = new Main().openFile();        System.out.println(str);             }         public String openFile() {        try {            FileInputStream inputStream = new FileInputStream("d:/a.txt");            int ch = inputStream.read();            System.out.println("aaa");            return "step1";        } catch (FileNotFoundException e) {            System.out.println("file not found");            return "step2";        }catch (IOException e) {            System.out.println("io exception");            return "step3";        }finally{            System.out.println("finally block");            //return "finally";        }    }}
复制代码


运行结果:


file not foundfinally blockstep2


可以看出,在 try 块中发生 FileNotFoundException 之后,就跳到第一个 catch 块,打印"file not found"信息,并将"step2"赋值给返回值,然后执行 finally 块,最后将返回值返回。


而当我们将 finally 代码块中 return 语句注释去掉,运行结果变为:


file not foundfinally blockfinally


返回值被重新覆盖了,所以在使用 try..catch..finally 块的时候,注意千万不要在 finally 块中使用 return,因为 finally 中的 return 会覆盖已有的返回值。

finally 块不会被执行的情况

前面说了无论是否捕获或处理异常,finally 块里的语句都会被执行。(当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行)


那有没有什么情况 finally 块不会被执行呢?


答案是有的,在以下 4 种特殊情况下,finally 块不会被执行:


  1. 在 finally 语句块中发生了异常。

  2. 在前面的代码中用了 System.exit()退出程序。

  3. 程序所在的线程死亡。

  4. 关闭 CPU。

四、异常的链化

在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致一连串的异常。假设 B 模块完成自己的逻辑需要调用 A 模块的方法,如果 A 模块发生异常,则 B 也将不能完成而发生异常,但是 B 在抛出异常时,会将 A 的异常信息掩盖掉,这将使得异常的根源信息丢失。异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。


异常链化以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带 Throwable 参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。

五、子类重写父类异常处理

当子类重写父类的带有 throws 声明的函数时,其 throws 声明的异常必须在父类异常的可控范围内——用于处理父类的 throws 方法的异常处理器,必须也适用于子类的这个带 throws 方法 。这是为了支持多态。


简单来说:父类方法 throws 的是 2 个异常,子类就不能 throws 3 个及以上的异常。父类 throws IOException,子类就必须 throws IOException 或者 IOException 的子类。


如果父类的方法声明的异常类型只有非运行时异常(运行时异常),则子类在重写该方法的时候声明的异常也只能有非运行时异常(运行时异常),不能含有运行时异常(非运行时异常)。

六、异常处理设计

1.只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程


谨慎地使用异常,异常捕获的代价非常高昂,异常使用过多会严重影响程序的性能。


2.切忌使用空catch块


在捕获了异常之后什么都不做,相当于忽略了这个异常。千万不要使用空的 catch 块,空的 catch 块意味着你在程序中隐藏了错误和异常,并且很可能导致程序出现不可控的执行结果。如果你非常肯定捕获到的异常不会以任何方式对程序造成影响,最好用 Log 日志将该异常进行记录,以便日后方便更新和维护。


3.检查异常和非检查异常的选择


一旦你决定抛出异常,你就要决定抛出什么异常。这里面的主要问题就是抛出检查异常还是非检查异常。


检查异常导致了太多的 try…catch 代码,可能有很多检查异常对开发人员来说是无法合理地进行处理的,比如 SQLException,而开发人员却不得不去进行 try…catch,这样就会导致经常出现这样一种情况:逻辑代码只有很少的几行,而进行异常捕获和处理的代码却有很多行。这样不仅导致逻辑代码阅读起来晦涩难懂,而且降低了程序的性能。


我个人建议尽量避免检查异常的使用,如果确实该异常情况的出现很普遍,需要提醒调用者注意处理的话,就使用检查异常;否则使用非检查异常。


因此,在一般情况下,我觉得尽量将检查异常转变为非检查异常交给上层处理。


4.注意catch块的顺序


不要把上层类的异常放在最前面的 catch 块。比如下面这段代码:


try {        FileInputStream inputStream = new FileInputStream("d:/a.txt");        int ch = inputStream.read();        System.out.println("aaa");        return "step1";    } catch (IOException e) {        System.out.println("io exception");                 return "step2";    }catch (FileNotFoundException e) {        System.out.println("file not found");                  return "step3";    }finally{        System.out.println("finally block");        //return "finally";    }
复制代码


第二个 catch 的 FileNotFoundException 将永远不会被捕获到,因为 FileNotFoundException 是 IOException 的子类。


5.不要将提供给用户看的信息放在异常信息里


比如下面这段代码:


public class Main {    public static void main(String[] args) {        try {            String user = null;            String pwd = null;            login(user,pwd);        } catch (Exception e) {            System.out.println(e.getMessage());        }             }         public static void login(String user,String pwd) {        if(user==null||pwd==null)            throw new NullPointerException("用户名或者密码为空");        //...    }}
复制代码


展示给用户错误提示信息最好不要跟程序混淆一起,比较好的方式是将所有错误提示信息放在一个配置文件中统一管理。


6.避免多次在日志信息中记录同一个异常


只在异常最开始发生的地方进行日志信息记录。很多情况下异常都是层层向上跑出的,如果在每次向上抛出的时候,都 Log 到日志系统中,则会导致无从查找异常发生的根源。


7.异常处理尽量放在高层进行


尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂。


8.在finally中释放资源


如果有使用文件读取、网络操作以及数据库操作等,记得在 finally 中释放资源。这样不仅会使得程序占用更少的资源,也会避免不必要的由于资源未释放而发生的异常情况。

总结

在程序设计中,进行异常处理是非常关键和重要的一部分。一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度。


参考资料:https://www.cnblogs.com/dolphin0520/p/3769804.html


创作不易,关注💖、点赞👍、收藏🎉就是对作者最大的鼓励👏,欢迎在下方评论留言🧐

发布于: 刚刚阅读数: 2
用户头像

猫的树

关注

记录及分享自己的一点点成长 2022.07.07 加入

「 奋力奔跑的 Java 小白🧐 」 「个人博客」👉 https://www.kirecatyu.top/ 「CSDN 」👉 https://blog.csdn.net/qq_34383510 「博客园 」👉 https://www.cnblogs.com/kire-cat/

评论

发布
暂无评论
【Java 基础你一定要掌握的知识点】Java异常处理和设计_Java_猫的树_InfoQ写作社区