是否有可能在Java中构建一段代码片段,使假设java.lang.ChuckNorrisException
无法捕捉?
想到的想法是使用例如拦截器或aspect-oriented programming。
答案 0 :(得分:308)
我没有试过这个,所以我不知道JVM是否会限制这样的东西,但也许你可以编译抛出ChuckNorrisException
的代码,但是在运行时提供了一个类定义不延伸Throwable的ChuckNorrisException
。
更新:
它不起作用。它会生成验证错误:
Exception in thread "main" java.lang.VerifyError: (class: TestThrow, method: ma\
in signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestThrow. Program will exit.
更新2:
实际上,如果禁用字节码验证程序,可以使其正常工作! (-Xverify:none
)
更新3:
对于那些在家中的人,这里是完整的脚本:
创建以下类:
public class ChuckNorrisException
extends RuntimeException // <- Comment out this line on second compilation
{
public ChuckNorrisException() { }
}
public class TestVillain {
public static void main(String[] args) {
try {
throw new ChuckNorrisException();
}
catch(Throwable t) {
System.out.println("Gotcha!");
}
finally {
System.out.println("The end.");
}
}
}
编译类:
javac -cp . TestVillain.java ChuckNorrisException.java
执行命令
java -cp . TestVillain
Gotcha!
The end.
注释掉“扩展RuntimeException”和仅重新编译ChuckNorrisException.java
:
javac -cp . ChuckNorrisException.java
执行命令
java -cp . TestVillain
Exception in thread "main" java.lang.VerifyError: (class: TestVillain, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestVillain. Program will exit.
无需验证即可运行:
java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"
答案 1 :(得分:115)
在思考了这个之后,我成功地创建了一个无法捕获的异常。我选择将它命名为JulesWinnfield
,而不是Chuck,因为它是一个蘑菇云铺设的母亲例外。此外,它可能不是你想到的,但它当然不能被抓住。观察:
public static class JulesWinnfield extends Exception
{
JulesWinnfield()
{
System.err.println("Say 'What' again! I dare you! I double dare you!");
System.exit(25-17); // And you shall know I am the LORD
}
}
public static void main(String[] args)
{
try
{
throw new JulesWinnfield();
}
catch(JulesWinnfield jw)
{
System.out.println("There's a word for that Jules - a bum");
}
}
Et瞧!未捕获的异常。
输出:
运行:
再说'什么'!我赌你!我量你不敢!
Java结果:8
建立成功(总时间:0秒)
当我有更多时间的时候,我会看到我是否也想不出别的东西。
另外,请检查一下:
public static class JulesWinnfield extends Exception
{
JulesWinnfield() throws JulesWinnfield, VincentVega
{
throw new VincentVega();
}
}
public static class VincentVega extends Exception
{
VincentVega() throws JulesWinnfield, VincentVega
{
throw new JulesWinnfield();
}
}
public static void main(String[] args) throws VincentVega
{
try
{
throw new JulesWinnfield();
}
catch(JulesWinnfield jw)
{
}
catch(VincentVega vv)
{
}
}
导致堆栈溢出 - 同样,异常仍未被捕获。
答案 2 :(得分:85)
有了这样的例外,显然必须使用构造函数中的System.exit(Integer.MIN_VALUE);
,因为如果你抛出这样的异常会发生这种情况;)
答案 3 :(得分:45)
任何代码都可以捕获Throwable。所以不,你创建的任何异常都将成为Throwable的子类,并且会被捕获。
答案 4 :(得分:35)
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(当然,技术上此异常从未实际抛出,但不能抛出正确的ChuckNorrisException
- 它会先抛出你。)
答案 5 :(得分:28)
您抛出的任何异常都必须扩展Throwable,因此可以始终捕获它。所以答案是否定的。
如果您想让它难以处理,您可以覆盖方法getCause(), getMessage()
,getStackTrace()
,toString()
以抛出另一个java.lang.ChuckNorrisException
。
答案 6 :(得分:15)
在构造函数中,您可以启动一个重复调用originalThread.stop (ChuckNorisException.this)
线程可以反复捕获异常,但会一直抛出它直到它死亡。
答案 7 :(得分:13)
没有。 Java中的所有异常都必须是java.lang.Throwable
的子类,虽然它可能不是一个好习惯,但你可以捕获每种类型的异常,如下所示:
try {
//Stuff
} catch ( Throwable T ){
//Doesn't matter what it was, I caught it.
}
有关详细信息,请参阅java.lang.Throwable文档。
如果您试图避免checked exceptions(必须明确处理的那些),那么您将需要子类Error或RuntimeException。
答案 8 :(得分:9)
实际上接受的答案并不是那么好,因为Java需要在没有验证的情况下运行,即代码在正常情况下无法运行。
AspectJ拯救真正的解决方案!
异常类:
package de.scrum_master.app;
public class ChuckNorrisException extends RuntimeException {
public ChuckNorrisException(String message) {
super(message);
}
}
<强>方面:强>
package de.scrum_master.aspect;
import de.scrum_master.app.ChuckNorrisException;
public aspect ChuckNorrisAspect {
before(ChuckNorrisException chuck) : handler(*) && args(chuck) {
System.out.println("Somebody is trying to catch Chuck Norris - LOL!");
throw chuck;
}
}
示例应用
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
catchAllMethod();
}
private static void catchAllMethod() {
try {
exceptionThrowingMethod();
}
catch (Throwable t) {
System.out.println("Gotcha, " + t.getClass().getSimpleName() + "!");
}
}
private static void exceptionThrowingMethod() {
throw new ChuckNorrisException("Catch me if you can!");
}
}
<强>输出:强>
Somebody is trying to catch Chuck Norris - LOL!
Exception in thread "main" de.scrum_master.app.ChuckNorrisException: Catch me if you can!
at de.scrum_master.app.Application.exceptionThrowingMethod(Application.java:18)
at de.scrum_master.app.Application.catchAllMethod(Application.java:10)
at de.scrum_master.app.Application.main(Application.java:5)
答案 9 :(得分:8)
主题的一个变体是令人惊讶的事实,即您可以从Java代码中抛出未声明的已检查异常。因为它没有在方法签名中声明,所以编译器不会让你自己捕获异常,尽管你可以把它作为java.lang.Exception来捕获。
这是一个帮助类,可以让你抛出任何声明的东西:
public class SneakyThrow {
public static RuntimeException sneak(Throwable t) {
throw SneakyThrow.<RuntimeException> throwGivenThrowable(t);
}
private static <T extends Throwable> RuntimeException throwGivenThrowable(Throwable t) throws T {
throw (T) t;
}
}
现在throw SneakyThrow.sneak(new ChuckNorrisException());
会抛出ChuckNorrisException,但编译器会抱怨
try {
throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}
关于捕获ChuckNorrisException是一个已检查异常时未抛出的异常。
答案 10 :(得分:6)
Java中唯一的ChuckNorrisException
应为OutOfMemoryError
和StackOverflowError
。
你可以实际“捕获”它们,以便在抛出异常时catch(OutOfMemoryError ex)
将执行,但该块会自动将异常重新抛出给调用者。
我不认为public class ChuckNorrisError extends Error
可以做到这一点,但你可以尝试一下。我找不到有关扩展Error
答案 11 :(得分:6)
Is it possible to construct a snippet of code in java that would make a hypothetical java.lang.ChuckNorrisException uncatchable?
是的,答案就在于:设计您的java.lang.ChuckNorrisException
,使其不是java.lang.Throwable
的实例。为什么?根据定义,一个不可抓取的对象是无法捕获的,因为你永远无法捕获永远不会抛出的东西。
答案 12 :(得分:3)
你可以将ChuckNorris保持在内部或私密状态,并将他封装起来或让他甩开......
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
答案 13 :(得分:3)
Java中的异常处理的两个基本问题是它使用异常的类型来指示是否应该基于它采取行动,并且基于异常采取行动的任何事物(即“捕获”它)是推定解决潜在的条件。如果有一个异常对象可以决定哪些处理程序应该执行的方法,以及到目前为止执行的处理程序是否已经清理了足够的东西以使当前方法满足其退出条件,那将是有用的。虽然这可以用于制造“无法捕获”的异常,但是两个更大的用途是(1)制作异常,只有当它们被实际知道如何处理它们的代码捕获时才会被视为处理,并且(2)允许为了合理处理finally
块中发生的异常(如果在FooException
展开期间finally
块期间BarException
,则两个异常都应该在调用堆栈中向上传播;两者都应该是可捕获的,但是应该继续展开,直到两者都被捕获)。不幸的是,我认为没有任何方法可以使现有的异常处理代码以这种方式工作而不会破坏事物。
答案 14 :(得分:1)
很容易在当前线程上模拟未捕获的异常。这将触发未捕获异常的常规行为,从而在语义上完成工作。但是,它不一定会停止当前线程的执行,因为实际上没有抛出任何异常。
Throwable exception = /* ... */;
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
currentThread.getUncaughtExceptionHandler();
uncaughtExceptionHandler.uncaughtException(currentThread, exception);
// May be reachable, depending on the uncaught exception handler.
这在(非常罕见的)情况下实际上很有用,例如,当需要正确的Error
处理时,但是从框架中调用方法来捕获(并丢弃)任何Throwable
。
答案 15 :(得分:0)
在finalize
中调用System.exit(1),然后从所有其他方法中抛出异常的副本,以便程序退出。