只捕捉不到ChuckNorrisException

是否有可能在Java中构造一段代码,使假设的java.lang.ChuckNorrisException无法捕获?

我想到的是使用拦截器或面向方面的编程

31651 次浏览

任何代码都可以捕获Throwable。所以不,无论你创建什么异常都会是Throwable的子类并且会被捕获。

不。Java中的所有异常都必须继承java.lang.Throwable,尽管这可能不是一个好的实践,但你可以像这样捕获每种类型的异常:

try {
//Stuff
} catch ( Throwable T ){
//Doesn't matter what it was, I caught it.
}

有关更多信息,请参阅java.lang.Throwable文档。

如果您试图避免已检查的异常(必须显式处理的那些),那么您将需要子类Error或RuntimeException。

对于这样的异常,显然必须从构造函数中使用System.exit(Integer.MIN_VALUE);,因为如果抛出这样的异常,就会发生这种情况;)

抛出的任何异常都必须扩展Throwable,因此总是可以捕获它。所以答案是否定的。

如果你想让它难以处理,你可以覆盖方法getCause(), getMessage()getStackTrace()toString()来抛出另一个java.lang.ChuckNorrisException

我没有尝试过这样做,所以我不知道JVM是否会限制这样的东西,但也许你可以编译抛出ChuckNorrisException的代码,但在运行时提供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:

实际上,如果您禁用字节码验证器,您可以让它工作!(# EYZ0)

更新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.

注释掉"extends 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"

在构造函数中,可以启动一个反复调用originalThread.stop (ChuckNorisException.this)的线程

线程可以重复捕获异常,但会一直抛出异常,直到异常死亡。

你可以把ChuckNorris隐藏起来,也可以把他封装起来,也可以把他吞下去……

# EYZ0

Java中只有# eyz0和OutOfMemoryError

实际上,您可以“捕获”它们,这意味着在抛出异常时将执行catch(OutOfMemoryError ex),但该块将自动将异常重新抛出给调用者。

我不认为public class ChuckNorrisError extends Error可以解决问题,但你可以试试。我没有找到关于扩展Error的文档

经过思考,我成功地创建了一个不可捕捉的异常。然而,我选择将其命名为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");
}
}

果不其然!未捕获异常。

输出:

运行:

再说一遍“什么”!我敢打赌!我真不敢相信你!

Java结果:8

BUILD SUCCESSFUL(总时间: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)
{
        

}
}

导致堆栈溢出-同样,异常仍然未捕获。

public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}

(当然,从技术上讲这个异常实际上从来没有被抛出,但是一个正确的ChuckNorrisException不能被抛出——它会先抛出你。)

Java中异常处理的两个基本问题是,它使用异常的类型来指示是否应该基于该异常采取操作,以及任何基于异常采取操作的东西(例如。“抓住它”)被假定为解决潜在的条件。如果有一种方法可以让异常对象决定应该执行哪些处理程序,以及到目前为止已经执行的处理程序是否已经对当前方法的退出条件进行了足够的清理,这将是很有用的。虽然这可以用来制造“不可捕捉”异常,两个更大的用途是(1)制造异常,只有当它们被实际上知道如何处理它们的代码捕获时才会被认为是处理的,(2)允许合理处理发生在finally块中的异常(如果在BarException的展开期间finally块中的FooException,两个异常都应该传播到调用堆栈;两者都应该是可捕获的,但unwind应该继续,直到两者都被捕获为止)。不幸的是,我不认为有任何方法可以使现有的异常处理代码以这种方式工作而不破坏一些东西。

# EYZ0

是的,答案是:设计你的java.lang.ChuckNorrisException,使它不是java.lang.Throwable的实例。为什么?从定义上讲,不可抛出的对象是不可捕获的,因为你永远不能捕获永远不能抛出的东西。

我的答案是基于@jtahlborn的想法,但它是一个完全工作的Java程序,可以打包到JAR文件中,甚至可以作为web应用程序的一部分部署到您最喜欢的应用程序服务器上。

首先,让我们定义ChuckNorrisException类,这样它就不会从一开始就崩溃JVM(顺便说一句,Chuck真的很喜欢崩溃JVM:)

package chuck;


import java.io.PrintStream;
import java.io.PrintWriter;


public class ChuckNorrisException extends Exception {


public ChuckNorrisException() {
}


@Override
public Throwable getCause() {
return null;
}


@Override
public String getMessage() {
return toString();
}


@Override
public void printStackTrace(PrintWriter s) {
super.printStackTrace(s);
}


@Override
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
}
}

现在使用Expendables类来构造它:

package chuck;


import javassist.*;


public class Expendables {


private static Class clz;


public static ChuckNorrisException getChuck() {
try {
if (clz == null) {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("chuck.ChuckNorrisException");
cc.setSuperclass(pool.get("java.lang.Object"));
clz = cc.toClass();
}
return (ChuckNorrisException)clz.newInstance();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}

最后是Main类来踢一些屁股:

package chuck;


public class Main {


public void roundhouseKick() throws Exception {
throw Expendables.getChuck();
}


public void foo() {
try {
roundhouseKick();
} catch (Throwable ex) {
System.out.println("Caught " + ex.toString());
}
}


public static void main(String[] args) {
try {
System.out.println("before");
new Main().foo();
System.out.println("after");
} finally {
System.out.println("finally");
}
}
}

使用以下命令编译并运行它:

java -Xverify:none -cp .:<path_to_javassist-3.9.0.GA.jar> chuck.Main

您将得到以下输出:

before
finally

这并不奇怪,毕竟这是一个回旋踢:)

这个主题的一个变体是您可以从 Java 代码中抛出未声明的检查异常这一令人惊讶的事实。因为它没有在方法签名中声明,所以编译器不允许您捕获异常本身,尽管您可以将它作为 java.lang 来捕获。例外。

这里有一个 helper 类,可以抛出任何已声明或未声明的内容:

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 是已检查异常,则捕获未引发的异常。

finalize中调用 System.exit (1) ,并从所有其他方法中抛出异常的副本,这样程序就会退出。

实际上,接受的答案并不那么好,因为 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)

在当前线程上很容易模拟未捕获的异常。这将触发未捕获异常的常规行为,从而在语义上完成工作。但是,它不一定会停止当前线程的执行,因为实际上没有抛出任何异常。

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来调用该方法。