在catch块中引发异常-是否会再次捕获?


180

这似乎是一个编程101的问题,我以为我知道答案,但是现在发现自己需要仔细检查。在下面的这段代码中,在第一个catch块中引发的异常是否会被下面的常规Exception catch块捕获?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

我一直认为答案是否定的,但是现在我有一些奇怪的行为可能是由这种情况引起的。对于大多数语言来说,答案可能是相同的,但是我正在使用Java。


2
也许您可以描述“奇怪的行为”?
Jeffrey L Whitledge,

您确定ApplicationException没有被抛出到其他地方并传播到该块吗?
sblundy

我在Eclipse IDE中注意到了这一点。强迫我将“引发新异常”放入try块中很累,但我不知道为什么。我过去没有这样做。我不明白为什么需要try块。Google上的许多示例表明人们不需要try块。是因为我在if语句中扔了吗?
djangofan 2013年

Answers:


213

否,因为新项throw不在try直接阻止中。


可以说,如果代码是这样的,请尝试{} catch(Exception e){System.err.println(“ In catch Exception:” + e.getClass()); } catch(IOException e){System.err.println(“在catch IOException中:” + e.getClass()); 并在try块中的代码生成IO异常,它会转到直接的常规Exception块,还是会飞到IOException catch块?
sofs1

4
@ user3705478该代码不会编译,以避免出现这种错误情况。通常,在同一个try块中捕获子类之后,不允许您捕获子类。
克里斯·杰斯特·杨

谢谢。所以我会得到一个编译时错误,对吗?到家时我会测试一下。
sofs1

这取决于@ user3705478,如果RuntimeExceptioncatch块中抛出a ,将没有编译错误。
Randy the Dev

@AndrewDunn我不认为这是什么user3705478的问题是关于,而是,如果父母的异常,会发生什么catch条款列出一个孩子的例外catch条款。我的理解是Java不允许这样做,并且它是在编译时捕获的。
克里斯·杰斯特·杨

71

不,很容易检查。

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

应打印:

在捕获IOException中:类java.io.IOException
终于
线程“主”中的异常java.lang.RuntimeException
        在Catch.main(Catch.java:8)

从技术上讲,这可能是编译器错误,依赖于实现,未指定的行为等。但是,JLS的定义很完善,而编译器对于这种简单的事情已经足够好了(泛型情况可能是另一回事)。

另请注意,如果在两个catch块之间交换,则不会编译。第二个陷阱将完全无法达到。

请注意,即使执行了catch块,finally块也始终会运行(除了愚蠢的情况,例如无限循环,通过工具接口进行附加并杀死线程,重写字节码等)。


3
finally当然,避免a的最明显方法是致电System.exit。:-P
克里斯·杰斯特·杨

3
@Chris Jester-Young for(;;);较短,包含在语言中,没有以副作用的方式引入太多,对我而言,更明显。
汤姆·哈特芬

1
System.exit对CPU更友好!:-O但是,是的,好的,显然这是一个主观标准。另外,我不知道您是一名标准高尔夫球手。;-)
克里斯·杰斯特·杨

28

Java语言规范在14.19.1节中说:

如果由于抛出值V而使try块的执行突然完成,则可以选择:

  • 如果V的运行时类型可分配给try语句的任何catch子句的Parameter,则选择第一个(最左侧)此类catch子句。将值V分配给所选catch子句的参数,并执行该catch子句的Block。如果该块正常完成,则try语句正常完成;如果该块由于某种原因突然完成,则try语句由于相同的原因突然完成。

参考:http : //java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

换句话说,第一个可以处理该异常的封闭捕获确实发生了,并且如果该捕获抛出异常,则该捕获不在原始尝试的任何其他捕获的范围内,因此他们将不会尝试处理该异常。

一个相关且令人困惑的事情是,在try- [catch] -finally结构中,finally块可能会引发异常,如果这样,则try或catch块引发的任何异常都会丢失。第一次看到它时,可能会造成混乱。


从Java 7开始,您只想补充一点,您可以避免使用try-with-resources。然后,如果tryAND finally都抛出,finally则被抑制,但也被添加到的异常中try。如果也catch抛出异常,除非您通过'addSuppressed'处理并添加try异常- 否则,您就不会碰运气。
LAFK说恢复莫妮卡

6

如果要从catch块引发异常,则必须通知您的方法/类/等。它需要抛出该异常。像这样:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

现在您的编译器不会对您大喊大叫了:)


4

不-正如Chris Jester-Young所说的那样,它将被扔到层次结构中的下一个try-catch。


2

如上所述,
我要补充的是,如果您无法查看正在发生的情况,如果您无法在调试器中重现该问题,则可以在重新引发新异常之前添加跟踪(具有良好的旧系统) .out.println更糟,否则使用类似log4j的良好日志系统)。


2

它不会被第二个catch块捕获。仅在try块中时才捕获每个异常。您可以嵌套尝试(不是一般来说这是一个好主意):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

我写了类似的代码。但是我不相信。我想在我的catch块中调用Thread.sleep()。但是Thread.sleep会抛出自身InterruptedException。像示例中所示那样做是正确的(最佳实践)吗?
riroo

1

不,因为所有catch都引用同一个try块,所以从catch块中抛出的内容将被封闭的try块捕获(可能在调用此方法的方法中)


-3

旧帖子,但“ e”变量必须唯一:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

5
这应该是评论而不是答案。它没有提供解决实际问题的任何方式-只是对OP问题的一种批评。
德里克·W
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.