【问题标题】:Can I catch multiple Java exceptions in the same catch clause?我可以在同一个 catch 子句中捕获多个 Java 异常吗?
【发布时间】:2011-03-30 14:00:30
【问题描述】:

在 Java 中,我想做这样的事情:

try {
    ...     
} catch (/* code to catch IllegalArgumentException, SecurityException, 
            IllegalAccessException, and NoSuchFieldException at the same time */) {
   someCode();
}

...而不是:

try {
    ...     
} catch (IllegalArgumentException e) {
    someCode();
} catch (SecurityException e) {
    someCode();
} catch (IllegalAccessException e) {
    someCode();
} catch (NoSuchFieldException e) {
    someCode();
}

有什么办法吗?

【问题讨论】:

    标签: java exception try-catch multi-catch


    【解决方案1】:

    这是可能的since Java 7。多捕获块的语法是:

    try { 
      ...
    } catch (IllegalArgumentException | SecurityException | IllegalAccessException |
                NoSuchFieldException e) { 
      someCode();
    }
    

    但请记住,如果所有异常都属于同一个类层次结构,您可以简单地捕获该基本异常类型。

    另外请注意,如果ExceptionB 直接或间接继承自ExceptionA,则不能在同一块中同时捕获ExceptionAExceptionB。编译器会抱怨:

    Alternatives in a multi-catch statement cannot be related by subclassing
      Alternative ExceptionB is a subclass of alternative ExceptionA
    

    解决此问题的方法是仅在异常列表中包含祖先异常,因为它也会捕获后代类型的异常。

    【讨论】:

    • T.T - 为什么要重新定义 bitwise or (|) 运算符?为什么不使用逗号或具有更相似含义的运算符logical or (||)?
    • @ArtOfWarfare 也许他们已经想出了泛型的multiple bounds 的语法后认为这不再重要了。
    • XOR 符号 (I) 与 OR (||) 不同,A | B 表示 A 或 B 不是 A || B 表示 A 或 B 或两者都表示,因此对于异常,它是异常 A 或异常 B,但不能同时表示两者。这就是为什么他们使用 XOR sing 而不是 OR 的原因,如果您放置 2 个异常,其中一个是另一个的子类型,您可以清楚地看到何时抛出异常
    • @user1512999 在 Java 中,按位 XOR 是 ^(插入符号),按位 OR 是 | (管道)docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
    • 值得一提的是,multi-catch 块中捕获的异常类型被评估为最衍生的公共父项
    【解决方案2】:

    不完全是在 Java 7 之前,但是,我会做这样的事情:

    Java 6 及之前的版本

    try {
      //.....
    } catch (Exception exc) {
      if (exc instanceof IllegalArgumentException || exc instanceof SecurityException || 
         exc instanceof IllegalAccessException || exc instanceof NoSuchFieldException ) {
    
         someCode();
    
      } else if (exc instanceof RuntimeException) {
         throw (RuntimeException) exc;     
    
      } else {
        throw new RuntimeException(exc);
      }
    
    }
    



    Java 7

    try {
      //.....
    } catch ( IllegalArgumentException | SecurityException |
             IllegalAccessException |NoSuchFieldException exc) {
      someCode();
    }
    

    【讨论】:

    • 请注意,您的 Java 6 示例破坏了编译器判断从哪里抛出什么的能力。
    • @MichaelBlume 是的,这还不错。您始终可以使用exc.getCause() 获得原始异常。作为旁注,Robert C. Martin(以及其他人)建议使用未经检查的异常(编译器不知道会从那里抛出什么样的异常);参考他的书Clean code中的Chapter 7: Error Handling
    • 在您的 Java 6 示例中,您不应该重新抛出原始异常而不是创建新的异常实例,即 throw exc 而不是 throw new RuntimeException(exc)
    • 从可读性的角度来看,这是非常糟糕的做法。
    • Instanceof操作有点费力,最好尽量避免。
    【解决方案3】:

    在 Java 7 中,您可以定义多个 catch 子句,例如:

    catch (IllegalArgumentException | SecurityException e)
    {
        ...
    }
    

    【讨论】:

      【解决方案4】:

      如果存在异常层次结构,您可以使用基类来捕获异常的所有子类。在退化的情况下,您可以通过以下方式捕获 所有 Java 异常:

      try {
         ...
      } catch (Exception e) {
         someCode();
      }
      

      在更常见的情况下,如果 RepositoryException 是基类而 PathNotFoundException 是派生类,那么:

      try {
         ...
      } catch (RepositoryException re) {
         someCode();
      } catch (Exception e) {
         someCode();
      }
      

      上面的代码将捕获 RepositoryException 和 PathNotFoundException 用于一种异常处理,并将所有其他异常集中在一起。 从 Java 7 开始,根据上面@OscarRyz 的回答:

      try { 
        ...
      } catch( IOException | SQLException ex ) { 
        ...
      }
      

      【讨论】:

      • BTW catch 子句按顺序处理,因此如果您将父异常类放在子类之前,则它永远不会被调用,例如:try { ... } catch (Exception e) { someCode(); } catch (RepositoryException re) { // 从未到达 }
      • 其实正是因为它永远无法到达,所以这样的代码甚至无法编译。
      【解决方案5】:

      不,在 Java 7 之前,每个客户一个。

      只要在所有情况下都采取相同的操作,就可以捕获超类,例如 java.lang.Exception。

      try {
          // some code
      } catch(Exception e) { //All exceptions are caught here as all are inheriting java.lang.Exception
          e.printStackTrace();
      }
      

      但这可能不是最佳做法。只有当您有实际处理它的策略时,您才应该捕获异常 - 并且记录和重新抛出不是“处理它”。如果您没有纠正措施,最好将其添加到方法签名中,并让它冒泡给可以处理这种情况的人。

      使用 JDK 7 及更高版本,您可以这样做:

      try {
          ...     
      } catch (IllegalArgumentException | SecurityException | IllegalAccessException | NoSuchFieldException e) {
          someCode();
      }
      

      【讨论】:

      • 我可以请你改写关于捕获 java.lang.Exception 的部分吗?我意识到这是一个例子,但我觉得有些人可能会读到这个答案并说,“哦,好吧,我会抓住异常然后”,而这可能不是他们想要(或应该)做的。跨度>
      • 我知道这一点,但我不想这样做...哦,好吧,我猜我会遇到 4 个问题,直到 Java 的下一个版本...跨度>
      • @duffymo:记录和重新抛出有什么问题?除了它使代码混乱之外,它相当于不捕获它,不是吗。从一般错误处理策略的角度来看。不好的是记录和重新抛出。
      • 我不考虑记录和重新处理任何事情。我更愿意让它冒泡给可以做一些有意义的事情的人。在这种情况下,异常不应逃逸的最后一层(例如 Web 应用程序中的控制器)应该是记录错误的层。
      • 只有我一个人觉得没有为我自动生成日志是很荒谬的吗?似乎每次一段代码可能抛出异常时,我们都必须编写同样愚蠢的日志消息。
      【解决方案6】:

      user454322 在 Java 6(即 Android)上的答案更简洁(但不那么冗长,也许不是首选)替代方法是捕获所有 Exceptions 并重新抛出 RuntimeExceptions。如果您计划在堆栈中进一步捕获其他类型的异常(除非您也重新抛出它们),这将不起作用,但会有效地捕获所有已检查异常。

      例如:

      try {
          // CODE THAT THROWS EXCEPTION
      } catch (Exception e) {
          if (e instanceof RuntimeException) {
              // this exception was not expected, so re-throw it
              throw e;
          } else {
              // YOUR CODE FOR ALL CHECKED EXCEPTIONS
          } 
      }
      

      话虽如此,为了冗长,最好设置一个布尔值或其他变量,并在此基础上在 try-catch 块之后执行一些代码。

      【讨论】:

      • 这种方法可以防止编译器确定“catch 块”是否可达。
      【解决方案7】:

      在 pre-7 怎么样:

        Boolean   caught = true;
        Exception e;
        try {
           ...
           caught = false;
        } catch (TransformerException te) {
           e = te;
        } catch (SocketException se) {
           e = se;
        } catch (IOException ie) {
           e = ie;
        }
        if (caught) {
           someCode(); // You can reference Exception e here.
        }
      

      【讨论】:

      • 应该是一个不错的解决方案。将caught 的最终控制权插入finally 块中如何?
      • 这需要比原始问题更多的行。
      【解决方案8】:

      很简单:

      try { 
        // Your code here.
      } catch (IllegalArgumentException | SecurityException | IllegalAccessException |
                  NoSuchFieldException e) { 
        // Handle exception here.
      }
      

      【讨论】:

        【解决方案9】:

        对于 kotlin,目前还不能,但他们已经考虑添加它:Source
        但现在,只是一个小技巧:

        try {
            // code
        } catch(ex:Exception) {
            when(ex) {
                is SomeException,
                is AnotherException -> {
                    // handle
                }
                else -> throw ex
            }
        }
        

        【讨论】:

          【解决方案10】:

          在异常层次结构中捕获恰好是父类的异常。 This is of course, bad practice。在您的情况下,常见的父异常恰好是 Exception 类,并且捕获作为 Exception 实例的任何异常确实是不好的做法 - NullPointerException 之类的异常通常是编程错误,通常应该通过检查空值来解决。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-06-28
            • 1970-01-01
            • 2011-07-30
            • 1970-01-01
            • 2012-01-16
            • 2019-09-30
            • 2014-05-26
            • 1970-01-01
            相关资源
            最近更新 更多