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

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


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


不。Java中的所有异常都必须成为Java .lang的子类。虽然这可能不是一个好的实践,但你可以像这样捕获每种类型的异常:

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的类定义,它不扩展Throwable。

更新:

不管用。它生成一个验证错误:

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:没有)

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

Run:

java -cp . TestVillain
Gotcha!
The end.

注释掉"extends RuntimeException",只重新编译ChuckNorrisException.java:

javac -cp . ChuckNorrisException.java

Run:

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的线程。停止(ChuckNorisException.this)

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


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

try {doChuckAction();} catch(ChuckNorrisException cne){/*做其他事情*/}


Java中唯一的chucknorrisexception应该是OutOfMemoryError和StackOverflowError。

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

我不认为公共类ChuckNorrisError extends 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不能被抛出——它会先抛出你。)


Two fundamental problems with exception handling in Java are that it uses the type of an exception to indicate whether action should be taken based upon it, and that anything which takes action based upon an exception (i.e. "catch"es it) is presumed to resolve the underlying condition. It would be useful to have a means by which an exception object could decide which handlers should execute, and whether the handlers that have executed so far have cleaned things up enough for the present method to satisfy its exit conditions. While this could be used to make "uncatchable" exceptions, two bigger uses would be to (1) make exceptions which will only be considered handled when they're caught by code that actually knows how to deal with them, and (2) allow for sensible handling of exceptions which occur in a finally block (if a FooException during a finally block during the unwinding of a BarException, both exceptions should propagate up the call stack; both should be catchable, but unwinding should continue until both have been caught). Unfortunately, I don't think there would be any way to make existing exception-handling code work that way without breaking things.


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

是的,这就是答案:设计你的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);
    }
}

现在让敢死队来构建它:

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.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;
  }
}

现在扔SneakyThrow。溜(新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.

这实际上在(非常罕见的)情况下很有用,例如当需要适当的错误处理时,但该方法从捕获(并丢弃)任何Throwable的框架中调用。