美文网首页
Java异常:概念区别

Java异常:概念区别

作者: 厨房里的工程师 | 来源:发表于2018-06-15 12:28 被阅读0次

前段时间读了Effective Java第三版中的异常,读了之后也没总结,很多知识点都是囫囵吞枣地理解。尤其是一些概念上的区别会直接影响如何使用Java异常,因此个人认为有必要了解相关的概念区别。文章很多问题来自Hollis的星球,有一些问题则是我自己在阅读过程中产生的。一些答案来自Hollis在星球里给出的解答或者是StackOverlowWikiBook等网站上的解答,我则以自己的理解来综合总结。

1. Error和Exception的区别

@georgios-gousios的在Exception Handling Bug Hazards on Android中的一张幻灯片准确地解释了错误和异常在Java中的区别。

The Java Exception Hierarchy

Throwable有三个重要的子类:

  • 错误:发生了非常严重的问题,大部分应用都应该推出执行而不是尝试这去解决这个问题。
  • 非受检异常:通常指NullPointerException这一类的编程错误或者是非法的参数。应用可以处理非受检异常并恢复,然后继续执行,或者至少在线程的run()方法中捕获并记录该非受检异常。
  • 受检异常:应用需要去捕获并处理这类异常,比如FileNotFoundExceptionTimeoutException

错误不应该被捕获或者处理(除了极少数特殊情况)。异常则是异常处理中的关键部分。Java文档对错误做了很好的解释:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.
错误是Throwable的子类,一个合理的应用不应该捕获指示了严重问题的错误,大部分这类错误都是非正常情况。

一起来看几个Error的子类和它们在Java文档中的注释:

  • AnnotationFormatError: 当注解分析器从一个类文件中读取注解并发现注解格式错误时,抛出AnnotationFormatError
  • AssertionError:抛出该异常用来表明Java断言错误。
  • LinkageErrorLinkageError的子类通常表明一个类和另一个类有着某种依赖关系;但是,在依赖类被编译以后,被依赖类被修改了以致无法与依赖类兼容(破坏了依赖关系)。
  • VirtualMachineError: 抛出并表明Java虚拟机宕机,或者表明Java虚拟机无法获取继续计算所必需的资源。

Effective Java 3中关于AssertionError的例子:
代码清单1:AssertionError禁止用户去初始化一个类:

public class UtilityClass {
    // Suppress default constructor for noninstantiability
    private UtilityClass() {
    throw new AssertionError();
}
     ...  // Remainder omitted
}

参考

2. 受检异常(Checked Exception)与非受检异常(Unchecked Exception)

2.1 受检异常

受检异常是一种我们必须捕获或在抛出方法中声明的异常。比如,java.io.IOException就是一个受检异常。为了更好地明白什么是受检异常,请参考下列代码:
代码清单2: 未处理的受检异常

public void ioOperation(boolean isResourceAvailable) {
    if (!isResourceAvailable) {
        throw new IOException();
    }
}

以上代码将无法通过编译,因为代码抛出了受检异常。产生的编译错误可以通过以下两种方式解决:

  • 捕获并处理该异常;
    代码清单3
    public void ioOperation(boolean isResourceAvailable) {
        try {
            if (!isResourceAvailable) {
                throw new IOException();
            }
        } catch(IOException e) {
            // Handle caught exceptions.
        }
    }
    
  • 在方法中声明抛出异常;
    代码清单4
    public void ioOperation(boolean isResourceAvailable) throws IOException {
        if (!isResourceAvailable) {
            throw new IOException();
        }
    }
    

在Java类结构中,如果一个异常继承自java.lang.Throwable而非java.lang.RuntimeException或者java.lang.Error,则该异常被认为是受检异常。所有应用和业务逻辑异常都应该是受检异常。

即使一个方法声明抛出一个异常,但实际上却没有抛出的情况是不可能的。调用者依然要处理该异常。受检异常的声明会产生多米诺骨牌效应,即调用了已定义方法的任何方法都要处理受检异常。

所以在编译期间,Java语言的编译器会会通过分析每一个方法的主体来检查一段程序是否包含了所有应用异常的处理器。假如通过执行该方法的主体,该方法会向调用者抛出一个异常,那么该异常必须被声明。编译器是如何知晓一个方法的主体是否能抛出异常的呢?这就简单了。在方法的主体中包含有对其他方法的调用,编译器会通过检查每个方法的签名来知晓它们抛出了什么异常。

2.1.1 为什么强制提供异常处理?

也许程序员会觉得强制异常处理很无聊,但是这迫使他们去思考所有的受检异常并提升代码的质量。编译期间对异常处理器是否出现的检查就是为了使应用开发人员的工作变得更简单。调试检查一个特定的异常是否有对应的捕获是一个漫长的过程。在传统的编程语言中,如C和C++,必须要有一个分开的错误处理调试。在Java中,我们可以肯定当一个应用异常被抛出,在程序的某个地方该异常一定会被处理。在C和C++中就需要去测试(译注:异常和错误是否被处理),在Java中这就无需测试,所以节约的时间就可以用于更有意义的测试,比如测试业务特征。

2.1.2 覆盖一个方法时可以声明什么异常?

关键字throws后声明的特定受检异常是实现者和用户之间合同的一部分。一个覆盖方法可以声明相同的异常,该异常的子类或者没有异常。

2.1.3 实现一个接口时可以声明什么异常?

涉及到接口时,接口的实现可能跳过throws语句,但是你如果想保留那就必须与接口声明兼容。换而言之,接口的实现必须抛出相同的异常或者是异常的子类,又或者是没有异常。

2.2 非受检异常

非受检、不用捕获或运行时异常可以在不被捕获和声明的情况下被抛出。
代码清单5

public void futureMethod() {
    throw new RuntimeException("This method is not yet implemented");
}

但是,你依然可以声明并捕获这类异常。运行时异常并非业务异常,它们通常和硬编码的问题相关联,比如数据错误、算术溢出、除以0等等。换而言之,这些错误无法被解决也无法预测。最凑名昭著的运行时异常就是NullPointerException。

一个运行时异常必须是RuntimeException或者Error的子类。

有时为了记录异常而去捕获所有异常是值得的,之后再将异常抛出。比如在servlet编程中,

相关文章

  • JAVA面试50讲之1:Exception底层原理是什么?

    目录 异常的概念 异常的使用 异常的原理 Error与Exception的区别 使用建议 一、异常的概念 java...

  • Java异常:概念区别

    前段时间读了Effective Java第三版中的异常,读了之后也没总结,很多知识点都是囫囵吞枣地理解。尤其是一些...

  • Java中异常和错误的区别及处理方式

    异常和错误的区别和联系 在Java中,异常和错误同属于一个类:Throwable。异常和错误都是Java异常处理重...

  • 异常

    异常的概念 Throwable是异常的超类,Exception(异常)和 Error(错误)都是 Java 异常处...

  • JavaGuide知识点整理——基础常见知识点(下)

    异常 Exception和Error有什么区别? 在java中,所有的异常都有一个共同的祖先,java.lang包...

  • 异常处理机制

    JAVA异常机制 一、基本概念在Java中这种在程序中运行时可能出现的一些错误称为异常。Java处理异常时,如果某...

  • 异常try,catch,finally,throw,throws

    java异常概念 java异常分类 异常的捕获和处理 运行期出现的错误是最难解决的. 观察错误名字和行号最重要! ...

  • Java的异常

    (1)基本概念 java的异常结构图 Throwable是所有异常的根,java.lang.Throwable E...

  • C++中的异常处理

    C语言异常处理 异常的概念 异常的说明程序在运行过程中可能产生异常异常(Exception)与Bug的区别异常是程...

  • Java 异常详解(一)

    一、Java异常的分类和类结构图 1,error和exception的区别 Java标准库内建了一些通用的异常,这...

网友评论

      本文标题:Java异常:概念区别

      本文链接:https://www.haomeiwen.com/subject/beapeftx.html