Exception和Error有什么区别?

请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?

  1. 如图Exception 和 Error都继承自Throwable类,由jdk api文档中对该类的描述可以知道它是异常处理机制的基本组成类型。可以由虚拟机抛出或者编码者自行抛出(throw)
    Throwable

    jdk8中文发翻译Throwable类的描述:Throwable类是Java语言中所有错误和异常的Throwable类。 只有作为此类(或其一个子类)的实例的对象由Java虚拟机抛出,或者可以由Java throw语句抛出。 类似地,只有这个类或其子类可以是catch子句中的参数类型。

  2. Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。

  3. Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。

  4. Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。

2.3.4条来自语录极客时间,Java核心技术36讲

如何处理这些异常?

  1. 在现实编程中我们一般使用 try-with-resources 和 multiple catch来进行一些异常处理(便利的特性),在编译时期,会自动生成相应的处理逻辑,比如,自动按照约定俗成 close 那些扩展了 AutoCloseable 或者 Closeable 的对象。
  2. try-with-resources这个说法是我在极客时间Java核心技术36讲看到后才知道的(自己比较low),只是一种处理Closeable实现类关闭资源的一种写法,简单写了一个例子可以发现其实这就是一中语法,这种语法可以自动编译帮忙编译最后关闭流的操作:
 public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("a"));
             BufferedWriter writer = new BufferedWriter(new FileWriter("a"))) {
            // Try-with-resources
        } catch (IOException e) {// Multiple catch
            // Handle it
        }
    }

编译后class

 public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new FileReader("a"));
            Throwable var2 = null;

            try {
                BufferedWriter writer = new BufferedWriter(new FileWriter("a"));
                Object var4 = null;
                if (writer != null) {
                    if (var4 != null) {
                        try {
                            writer.close();
                        } catch (Throwable var15) {
                            ((Throwable)var4).addSuppressed(var15);
                        }
                    } else {
                        writer.close();
                    }
                }
            } catch (Throwable var16) {
                var2 = var16;
                throw var16;
            } finally {
                if (br != null) {
                    if (var2 != null) {
                        try {
                            br.close();
                        } catch (Throwable var14) {
                            var2.addSuppressed(var14);
                        }
                    } else {
                        br.close();
                    }
                }

            }
        } catch (IOException var18) {
            ;
        }

    }

参考Java7里try-with-resources分析

异常处理的两个基本原则(来自语录极客时间,Java核心技术36讲)

  1. 尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常

    这是因为在日常的开发和合作中,我们读代码的机会往往超过写代码,软件工程是门协作的艺术,所以我们有义务让自己的代码能够直观地体现出尽量多的信息,而泛泛的 Exception 之类,恰恰隐藏了我们的目的。另外,我们也要保证程序不会捕获到我们不希望捕获的异常。比如,你可能更希望 RuntimeException 被扩散出来,而不是被捕获。

  2. 不要生吞(swallow)异常。这是异常处理中要特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。

    如果我们不把异常抛出来,或者也没有输出到日志(Logger)之类,程序可能在后续代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。

  3. 在写程序时可以通过Objects类(jdk提供),或者断言等提前判断问题,如空指针异常的一些值的处理Objects. requireNonNull(filename);而不是使用之后有问题再系统抛出异常,这可能就会不直观的找到问题所在。即:Throw early, catch late 原则

可参考他人总结

如何定义自定义异常?(来自语录极客时间,Java核心技术36讲)

  • 有的时候,我们会根据需要自定义异常,这个时候除了保证提供足够的信息,还有两点需要考虑:
    1. 是否需要定义成 Checked Exception,因为这种类型设计的初衷更是为了从异常情况恢复,作为异常设计者,我们往往有充足信息进行分类。
    2. 在保证诊断信息足够的同时,也要考虑避免包含敏感信息,因为那样可能导致潜在的安全问题。如果我们看 Java 的标准类库,你可能注意到类似 java.net.ConnectException,出错信息是类似“ Connection refused (Connection refused)”,而不包含具体的机器名、IP、端口等,一个重要考量就是信息安全。类似的情况在日志中也有,比如,用户数据一般是不可以输出到日志里面的。
  • 业界有一种争论(甚至可以算是某种程度的共识),Java 语言的 Checked Exception 也许是个设计错误,反对者列举了几点:
    1. Checked Exception 的假设是我们捕获了异常,然后恢复程序。但是,其实我们大多数情况下,根本就不可能恢复。Checked Exception 的使用,已经大大偏离了最初的设计目的。
    2. Checked Exception 不兼容 functional 编程,如果你写过 Lambda/Stream 代码,相信深有体会。

性能角度来审视异常

  1. try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。

  2. Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。

    所以,对于部分追求极致性能的底层类库,有种方式是尝试创建不进行栈快照的 Exception。这本身也存在争议,因为这样做的假设在于,我创建异常时知道未来是否需要堆栈。问题是,实际上可能吗?小范围或许可能,但是在大规模项目中,这么做可能不是个理智的选择。如果需要堆栈,但又没有收集这些信息,在复杂情况下,尤其是类似微服务这种分布式系统,这会大大增加诊断的难度。
    当我们的服务出现反应变慢、吞吐量下降的时候,检查发生最频繁的 Exception 也是一种思路。关于诊断后台变慢的问题,我会在后面的 Java 性能基础模块中系统探讨。

    看到目前为止,在我待过的公司中大部分都是抛出自定义业务异常,然后拦截异常处理,那么在性能的角度是否这样会造成性能下降呢?为此我搜索了一下是否使用抛出业务异常的方式来处理业务呢?
    其中看到一个为什么 Java 中要使用 Checked Exceptions,个人更赞同robbin的观点,由于经验不足,编程思想感觉也不到位就不多做评价。

    在jdk文档中提到:throwable在创建时包含其线程的执行堆栈的快照。 它还可以包含一个消息字符串,其中提供有关错误的更多信息。 随着时间的推移,一个可以抛出的其他可抛物线可以被传播。 最后,throwable也可能包含一个原因 :另一个可抛出的,导致这个可抛出的构造。 这种因果信息的记录被称为链接的异常设施,因为原因本身可能有原因等等,导致“链”的异常,每个异常都由另一个导致。

问题:NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

感觉讲的还是很详细了
NoClassDefFoundError 和 ClassNotFoundException 有什么区别

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

niubility锐

觉得有用的话鼓励鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值