架空のJava.lang.ChuckNorrisException
をキャッチできないコードのスニペットを Java で作成することは可能ですか?
思いついたのは、たとえばインターセプターや アスペクト指向プログラミング を使用することです。
私はこれを試していないので、 JVM がこのような制限をするかどうかはわかりませんが、ChuckNorrisException
をスローするコードをコンパイルできますが、実行時にChuckNorrisException
のクラス定義を提供できますwhichは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: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」とrecompile ChuckNorrisException.Java
onlyをコメントアウトします。
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"
これを熟考した後、キャッチできない例外を作成しました。ただし、ChuckではなくJulesWinnfield
という名前を付けることを選択しました。これは、1つのキノコ雲配置母例外であるためです。さらに、それはあなたが念頭に置いていた正確なものではないかもしれませんが、それは確かにキャッチすることはできません。観察する:
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)
{
}
}
スタックオーバーフローを引き起こします-再び、例外はキャッチされません。
このような例外がある場合、コンストラクターからSystem.exit(Integer.MIN_VALUE);
を使用することが明らかに必須になります。これは、このような例外をスローした場合に起こることだからです;)
どのコードでも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をクラッシュさせないようにします(チャックは本当に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
驚きはありません-結局、ラウンドハウスキックです:)
コンストラクターでは、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がチェック例外である場合にスローされない例外をキャッチすることについて。
Javaの唯一のChuckNorrisException
sは、OutOfMemoryError
およびStackOverflowError
でなければなりません。
実際には、例外がスローされた場合にcatch(OutOfMemoryError ex)
が実行されるという意味でそれらを「キャッチ」できますが、そのブロックは呼び出し元に例外を自動的に再スローします。
public class ChuckNorrisError extends Error
がトリックを行うとは思いませんが、試してみてください。 Error
の拡張に関するドキュメントが見つかりませんでした
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
を設計します。どうして?スローできないオブジェクトは、スローできないものをキャッチできないため、定義上キャッチできません。
チャック・ノリスを社内またはプライベートに保ち、カプセル化または飲み込むことができます...
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Javaでの例外処理に関する2つの基本的な問題は、例外のタイプを使用して、それに基づいてアクションを実行するかどうかを示すこと、および例外に基づいてアクションを実行するもの(つまり "catch" esそれは)根本的な状態を解決すると推定されます。例外オブジェクトがどのハンドラーを実行するか、およびこれまでに実行したハンドラーが、現在のメソッドが終了条件を満たすのに十分なものをクリーンアップしたかどうかを決定できる手段があると便利です。これを使用して「キャッチできない」例外を作成することもできますが、2つの大きな用途は、(1)実際に対処方法を知っているコードによってキャッチされた場合にのみ処理されると見なされる例外を作成し、(2)許可することですfinally
ブロックで発生する例外の賢明な処理のために(FooException
の巻き戻し中に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
処理が必要であるが、メソッドがThrowable
をキャッチ(および破棄)するフレームワークから呼び出される場合)で実際に役立ちます。
finalize
でSystem.exit(1)を呼び出し、他のすべてのメソッドから例外のコピーをスローするだけで、プログラムが終了します。