【问题标题】:What does "error: unreported exception <XXX>; must be caught or declared to be thrown" mean and how do I fix it?“错误:未报告的异常 <XXX>; 必须被捕获或声明被抛出”是什么意思,我该如何解决?
【发布时间】:2022-06-25 14:51:53
【问题描述】:

新的 Java 程序员经常遇到这样的错误:

"error: unreported exception <XXX>; must be caught or declared to be thrown" 

其中 XXX 是某个异常类的名称。

请解释一下:

  • 编译错误消息的含义,
  • 此错误背后的 Java 概念,以及
  • 如何解决。

【问题讨论】:

    标签: java compiler-errors uncaught-exception checked-exceptions


    【解决方案1】:

    首先要做的事情。这是一个编译错误而不是例外。您应该在编译时看到它。

    (如果您在运行时异常消息中看到它,那可能是因为您正在运行一些包含编译错误的代码。返回并修复编译错误,并在您的 IDE 中找到并设置防止它运行代码时出现编译错误。)


    这个问题的简短回答是:

    • 错误消息表明存在此错误的语句正在抛出(或传播)已检查异常,并且未正确处理异常(XXX)。

    • 解决方案是通过以下任一方式处理异常:

      • 使用try ... catch 语句捕获和处理它,或者
      • 声明封闭方法或构造函数throwsit1

    1 - 在某些极端情况下您无法做到这一点。阅读其余答案!


    已检查与未检查的异常

    在 Java 中,异常由 java.lang.Throwable 类的派生类表示。异常分为两类:

    • 已检查的异常ThrowableException 及其子类,RuntimeException 及其子类除外。
    • 未经检查的异常是所有其他异常;即Error 及其子类,RuntimeException 及其子类。

    (在上面,“子类”包括直接子类和间接子类。)

    已检查异常和未检查异常之间的区别在于,已检查异常必须在它们发生的封闭方法或构造函数中“处理”,但未检查异常不需要处理。

    (问:如何知道是否检查了异常?答:找到异常类的javadoc,并查看其父类。)

    如何处理(已检查)异常

    从 Java 语言的角度来看,有两种方法可以处理“满足”编译器的异常:

    1. 您可以在try ... catch 语句中捕获异常。例如:

      public void doThings() {
          try {
              // do some things
              if (someFlag) {
                  throw new IOException("cannot read something");
              }
              // do more things
          } catch (IOException ex) {
              // deal with it    <<<=== HERE
          }
      }
      

      在上面,我们将抛出(检查的)IOException 的语句放在try 的主体中。然后我们写了一个catch 子句来捕获异常。 (我们可以捕获 IOException 的超类 ...但在这种情况下,这将是 Exception 并且捕获 Exception 是个坏主意。)

    2. 你可以声明封闭方法throws异常

      public void doThings() throws IOException {
          // do some things
          if (someFlag) {
              throw new IOException("cannot read something");
          }
          // do more things
      }  
      

      在上面我们已经声明doThings() 抛出IOException。这意味着任何调用doThings() 方法的代码都必须处理异常。简而言之,我们将处理异常的问题传递给调用者。

    这些事情中哪些是正确的做法?

    这取决于上下文。但是,一般原则是您应该在代码中能够适当处理异常的级别处理异常。而这又取决于异常处理代码将要做什么(HERE)。能恢复吗?它可以放弃当前的请求吗?它应该停止应用程序吗?

    解决问题

    回顾一下。编译错误的意思是:

    • 您的代码已引发检查异常,或调用了引发检查异常的某些方法或构造函数,并且
    • 它没有处理异常,通过捕获它或按照 Java 语言的要求声明它。

    你的解决过程应该是:

    1. 了解异常的含义,以及为什么会抛出它。
    2. 根据1,决定正确的处理方式。
    3. 在 2 的基础上,对您的代码进行相关更改。

    有例外的不良做法

    有几件事是你应该避免做的:

    • 不要将Exception(或Throwable)作为捕获异常列表的捷径。如果你这样做了,你就有责任捕捉到你不期望的东西(比如 unchecked NullPointerException),然后在你不应该的时候尝试恢复。

    • 不要将方法声明为throws Exception。这迫使被调用者(可能)处理任何已检查的异常......这是一场噩梦。

    • 不要压制异常。例如

      try { 
          ...
      } catch (NullPointerException ex) {
      }
      

      如果您压制异常,您可能会使触发它们的运行时错误更难诊断。你在销毁证据。

    边缘情况

    在某些情况下处理检查的异常是一个问题。一种特殊情况是static 初始化程序中的检查异常。例如:

    private static final FileInputStream input = new FileInputStream("foo.txt");
    

    FileInputStream 被声明为throws FileNotFoundException ...这是一个检查异常。但是由于上面是一个字段声明,Java 语言的语法,不允许我们将声明放在 try ... catch 中。并且没有合适的(封闭的)方法或构造函数……因为这段代码是在 class 初始化时运行的。

    一种解决方案是使用static 块;例如:

    private static final FileInputStream input;
    
    static {
       FileInputStream temp = null;
       try {
           temp = new FileInputStream("foo.txt");
       } catch (FileNotFoundException ex) {
           // log the error rather than squashing it
       }
       input = temp;   // Note that we need a single point of assignment to 'input'
    }
    

    (在实际代码中有更好的方法来处理上述场景,但这不是这个例子的重点。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多