前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解Exception和Error的区别

详解Exception和Error的区别

原创
作者头像
刺槐儿
修改2023-11-30 07:53:11
2.1K1
修改2023-11-30 07:53:11
举报
文章被收录于专栏:技术路线技术路线

什么是Exception和Error

Error

Error是java程序运行中不可预料的非正常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等;

Exception

Exception是java程序运行中可预料的异常情况,可以获取到这种异常,并且对这种异常进行业务外的处理。Exception又分为检查性异常和非检查性异常。

  1. 检查性异常:没有继承RuntimeException的Exception属于检查异常,这类问题在编译期就可以确定的问题,如FileNotFoundException、IOException。这类异常必须在编写代码时,使用try-catch捕获(比如:IOException异常)
  2. 非检查异常:继承了RuntimeException的Exception,也叫运行时异常,这类问题大部分属于逻辑问题,如数组越界、空指针异常,只有运行时才能知道的问题,异常在编译时不会检查。这种异常是在代码编写或者使用过程中通过规范可以避免发生的。

Exception和Error有什么区别?

可以从以下四个方面进行回答:

  • 相同点和不同点
  • 异常的分类
  • 异常处理关键字
  • 异常处理的原则

一、相同点和不同点

相同点:Exception和Error都继承了Throwable类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

不同点:Exception 和 Error 体现了不同异常情况的分类。可以说Error是天灾,出现了也恢复不了;Exception是人祸,出现了可以有对应的解决措施。"Error"通常表示严重的问题,很难通过程序来处理,而"Exception"则更广泛地表示可以通过代码处理的异常情况。

二、异常的分类

异常可以分为三个主要类别:

  1. 检查异常(Checked Exception):这些异常在编译时被检查,程序员被要求显式地处理它们或者在方法签名中使用throws关键字声明它们。例如:IOException
  2. 运行时异常(Unchecked Exception):这些异常通常是由程序逻辑错误引起的,不需要在代码中显式地捕获或声明。例如:NullPointerExceptionArithmeticException
  3. 错误(Error):表示严重的问题,通常是系统级的问题,不太可能通过程序来恢复。例如:OutOfMemoryError

三、异常处理关键字

在Java中,异常处理使用以下关键字:

  1. try: 包含可能抛出异常的代码块。
  2. catch: 用于捕获并处理特定类型的异常。
  3. finally: 包含无论是否发生异常都会执行的代码块。
  4. throw: 用于手动抛出异常。
  5. throws: 用于在方法签名中声明可能抛出的异常。

四、日常开发中关于异常的处理原则

尽量不要捕获通用异常

比如直接捕获Exception或Throwable,不利于定位异常位置,这可能会隐藏程序中的潜在问题。在业务开发中更建议自定义异常,可以根据异常快速定位业务问题;

代码语言:java
复制
try {
  // 业务代码
  // …
  Thread.sleep(1000L);
} catch (Exception e) {
  // Ignore it
}

在这里是 Thread.sleep() 抛出的 InterruptedException。这是因为在日常的开发和合作中,我们读代码的机会往往超过写代码

尽量不要生吞(swallow)异常

要不然会出现异常难以诊断的诡异情况。在业务开发中哪怕不抛出异常,也要在异常位置打出关键日志。

生吞异常,往往是基于假设这段代码可能不会发生,或者感觉忽略异常是无所谓的,但是千万不要在产品代码做这种假设!如果不把异常抛出来,或者也没有输出到日志(Logger)之类,程序可能在后续代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。

代码语言:java
复制
try {
   // 业务代码
   // …
} catch (IOException e) {
    e.printStackTrace();
}

这段代码是没有任何问题的,但在开发中通常都不允许这样处理。这是因为printStackTrace()采用的是标准出错(STERR)的输出选项,很难判断出到底输出到哪里去了。如果程序就会抛出异常,但是由于没有第一时间暴露出问题,堆栈信息可能非常令人费解,往往需要相对复杂的定位。

在发现问题的时候,第一时间抛出,能够更加清晰地反映问题。尤其是对于分布式系统,如果发生异常,但是无法找到堆栈轨迹(stacktrace),这纯属是为诊断设置障碍。所以,最好使用产品日志,详细地输出到日志系统里。

考虑异常捕获在产生额外的开销

异常捕获在性能角度考虑会产生额外的开销,所以也要注意尽量不要捕获非必要的代码,捕获范围尽量小。从性能角度来审视一下 Java 的异常处理机制,这里有两个可能会相对昂贵的地方:

  1. try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。
  2. Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。
其他处理原则
  1. 捕获精确的异常:catch块中捕获特定类型的异常,以确保只处理程序能够处理的异常,而将其他异常传播到上一层。
  2. 使用finally进行清理操作: 如果有资源需要释放,最好在finally块中进行清理,以确保资源被正确释放,即使发生异常也能执行清理操作。
  3. 适度使用异常: 异常应该用于处理真正的异常情况,而不应该被用作控制流程的手段。
  4. 异常日志记录:catch块中记录异常信息,以便在调试和维护时能够更好地理解发生的问题。

补充

NoClassDefFoundError 和 ClassNotFoundException 的区别

这也是一个比较经典异常处理的面试题目。NoClassDefFoundErrorClassNotFoundException 都是 Java 中与类加载有关的异常,但它们发生的上下文和原因略有不同。

NoClassDefFoundError与ClassNotFoundException
  1. 发生场景: NoClassDefFoundError 在运行时发生,通常是在程序运行过程中,JVM 尝试加载某个类的字节码文件时发现该类的定义(Class Definition)存在,但在运行时却找不到该类的定义。 ClassNotFoundException 同样在运行时发生,它表示在运行时试图通过类的字符串名称加载类时,找不到对应的类。
  2. 原因: NoClassDefFoundError:这通常是因为在编译时存在对某个类的引用,但在运行时找不到该类的定义。可能是在编译时存在该类,但在运行时的 classpath 中找不到该类的字节码文件。 ClassNotFoundException:这通常是由于使用 Class.forName()ClassLoader.loadClass() 或类似的方法尝试加载类时,指定的类名字符串不对应于任何类。
区别总结:
  • NoClassDefFoundError 发生在运行时,表示在运行时找不到某个类的定义。NoClassDefFoundError 可能是因为在编译时存在该类,但在运行时的 classpath 中找不到该类的字节码文件。
  • ClassNotFoundException 同样在运行时发生,表示在运行时试图通过类的字符串名称加载类时找不到对应的类。ClassNotFoundException 可能是由于尝试通过类名字符串加载类时,指定的类名不对应于任何类。代码示例:
代码语言:java
复制
// NoClassDefFoundError 示例
public class ExampleNoClassDefFoundError {
    public static void main(String[] args) {
        try {
            // 编译时存在类,但运行时找不到
            Class.forName("com.example.NonExistentClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上述示例中,ExampleNoClassDefFoundError 中使用 Class.forName() 尝试加载一个编译时存在但运行时找不到的类,可能导致 NoClassDefFoundError

代码语言:java
复制
// ClassNotFoundException 示例
public class ExampleClassNotFoundException {
    public static void main(String[] args) {
        try {
            // 试图加载不存在的类
            ClassLoader.getSystemClassLoader().loadClass("com.example.NonExistentClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上述示例中, ExampleClassNotFoundException 中使用 ClassLoader.loadClass() 尝试加载一个不存在的类,可能导致 ClassNotFoundException

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Exception和Error
    • Error
      • Exception
      • Exception和Error有什么区别?
        • 一、相同点和不同点
          • 二、异常的分类
            • 三、异常处理关键字
              • 四、日常开发中关于异常的处理原则
                • 尽量不要捕获通用异常
                • 尽量不要生吞(swallow)异常
                • 考虑异常捕获在产生额外的开销
                • 其他处理原则
            • 补充
              • NoClassDefFoundError 和 ClassNotFoundException 的区别
                • NoClassDefFoundError与ClassNotFoundException
                • 区别总结:
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档