是否可以在Java中构造一段代码,使假设的无法java.lang.ChuckNorrisException
捕获?
想到的想法是使用例如拦截器或面向方面的编程。
我没有尝试过,所以我不知道JVM是否会限制这样的东西,但也许你可以编译 throws 的代码,但在运行时提供一个不扩展 ThrowableChuckNorrisException
的类定义。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.
注释掉“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"
深思熟虑后,我成功创建了一个无法捕获的异常。然而,我选择将其命名为JulesWinnfield
Chuck,而不是 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
构建成功(总时间: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)
{
}
}
导致堆栈溢出 - 同样,异常仍然未被捕获。
对于这样的异常,显然必须使用System.exit(Integer.MIN_VALUE);
构造函数中的 a ,因为如果抛出这样的异常就会发生这种情况;)
任何代码都可以捕获 Throwable。所以不,您创建的任何异常都将成为 Throwable 的子类,并且会被捕获。
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(当然,从技术上讲,这个异常实际上从未被抛出,但ChuckNorrisException
不能抛出一个正确的——它首先抛出你。)
您抛出的任何异常都必须扩展 Throwable,因此它总是可以被捕获。所以答案是否定的。
如果你想让它难以处理,你可以重写方法getCause(), getMessage()
, getStackTrace()
,toString()
来抛出另一个java.lang.ChuckNorrisException
.
我的回答是基于@jtahlborn 的想法,但它是一个完整的Java程序,可以打包到JAR文件中,甚至可以作为Web 应用程序的一部分部署到您最喜欢的应用程序服务器上。
首先,让我们定义ChuckNorrisException
类,这样它就不会从一开始就崩溃 JVM(Chuck 真的很喜欢崩溃 JVM BTW :)
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
毫不奇怪 - 毕竟这是一个回旋踢:)
在构造函数中,您可以启动一个重复调用的线程originalThread.stop (ChuckNorisException.this)
线程可以反复捕获异常,但会一直抛出异常,直到它死亡。
不可以。Java 中的所有异常都必须是子类java.lang.Throwable
,尽管这可能不是一个好习惯,但您可以像这样捕获每种类型的异常:
try {
//Stuff
} catch ( Throwable T ){
//Doesn't matter what it was, I caught it.
}
有关更多信息,请参阅java.lang.Throwable文档。
如果您试图避免检查异常(必须显式处理的异常),那么您将希望继承 Error 或 RuntimeException。
实际上接受的答案并不是那么好,因为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)
该主题的一个变体是令人惊讶的事实,即您可以从 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 是已检查的异常,则捕获未抛出的异常。
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
. 为什么?不可投掷的对象根据定义是不可捕获的,因为您永远无法捕获永远无法投掷的东西。
Java 中唯一ChuckNorrisException
的 s 应该是OutOfMemoryError
and StackOverflowError
。
您实际上可以“捕获”它们,即 acatch(OutOfMemoryError ex)
将在抛出异常的情况下执行,但该块将自动将异常重新抛出给调用者。
我不认为这public class ChuckNorrisError extends Error
有诀窍,但你可以试一试。我没有找到关于扩展的文档Error
您可以将 ChuckNorris 保持在内部或私密状态,然后封装他或吞下他……
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Java 中异常处理的两个基本问题是,它使用异常的类型来指示是否应该基于它采取行动,并且假定任何基于异常采取行动的事物(即“捕获”它)都可以解决基础条件。如果有一种方法可以让异常对象决定应该执行哪些处理程序,以及到目前为止已经执行的处理程序是否已经清理了足够的东西以使当前方法满足其退出条件,这将是有用的。虽然这可以用来产生“无法捕获”的异常,但两个更大的用途是(1)产生只有当它们被真正知道如何处理它们的代码捕获时才会被视为已处理的异常,finally
FooException
在 a 展开期间的finally
块期间BarException
,两个异常都应向上传播调用堆栈;两者都应该是可捕获的,但应继续展开直到两者都被捕获)。不幸的是,我认为没有任何方法可以使现有的异常处理代码以这种方式工作而不破坏事物。
很容易在当前线程上模拟未捕获的异常。这将触发未捕获异常的常规行为,从而在语义上完成工作。但是,它不一定会停止当前线程的执行,因为实际上没有抛出异常。
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
处理时,但该方法是从捕获(并丢弃) any 的框架调用的Throwable
。
在 中调用 System.exit(1) finalize
,然后从所有其他方法中抛出异常的副本,以便程序退出。