Javaが静的初期化ブロックからチェック例外をスローすることを許可しないのはなぜですか?この設計決定の背後にある理由は何でしたか?
Javaが静的初期化ブロックからチェック例外をスローすることを許可しないのはなぜですか?この設計決定の背後にある理由は何でしたか?
回答:
ソースでこれらのチェック済み例外を処理することができないためです。初期化プロセスを制御することはできず、静的{}ブロックをソースから呼び出すことができないため、ブロックをtry-catchで囲むことができます。
チェックされた例外が示すエラーは処理できないため、チェックされた例外の静的ブロックのスローを禁止することにしました。
静的ブロックは、チェックされた例外をスローしてはなりませんが、チェックされていない/実行時例外をスローすることを許可します。しかし、上記の理由によると、これらのいずれも処理できません。
要約すると、この制限により、開発者がアプリケーションを回復できないエラーを引き起こす可能性のある何かを構築することが防止されます(または少なくとも困難になります)。
static { if(1 < 10) { throw new NullPointerException(); } }
チェックされた例外をキャッチし、チェックされていない例外として再スローすることで、問題を回避できます。このチェックされていない例外クラスは、ラッパーとしてうまく機能しますjava.lang.ExceptionInInitializerError
。
サンプルコード:
protected static class _YieldCurveConfigHelperSingleton {
public static YieldCurveConfigHelper _staticInstance;
static {
try {
_staticInstance = new YieldCurveConfigHelper();
}
catch (IOException | SAXException | JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
}
catch (Exception e) {
代わりに。
System.exit(...)
(または同等の)は、あなたの唯一のオプションである
次のようにする必要があります(これは有効なJavaコードではありません)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
しかし、あなたはそれをどこでキャッチするか?チェックされた例外はキャッチする必要があります。クラスを初期化する可能性のあるいくつかの例を想像してください(または、クラスが既に初期化されているため、そうでない場合があります)。それがもたらす複雑さの注意を引くために、別の静的初期化子に例を置きます。
static {
try {
ClassA a = new ClassA();
Class<ClassB> clazz = Class.forName(ClassB.class);
String something = ClassC.SOME_STATIC_FIELD;
} catch (Exception oops) {
// anybody knows which type might occur?
}
}
そしてもう一つの厄介なこと-
interface MyInterface {
final static ClassA a = new ClassA();
}
ClassAに静的イニシャライザがチェックされた例外をスローしていると想像してください。この場合、MyInterface(「非表示」の静的イニシャライザを持つインターフェイス)は、例外をスローするか、それを処理する必要があります-インターフェイスでの例外処理?そのままにしておくとよいでしょう。
main
チェックされた例外をスローできます。明らかにそれらを処理することはできません。
main()
、スタックトレースがに例外が出力されてからが呼び出されると想定していSystem.err
ますSystem.exit()
。結局のところ、この質問に対する答えはおそらく「Javaデザイナーがそう言ったから」です。
Javaが静的初期化ブロックからチェック例外をスローすることを許可しないのはなぜですか?
技術的には、これを行うことができます。ただし、チェックされた例外はブロック内でキャッチする必要があります。チェックされた例外は、ブロックの外に伝播することはできません。
技術的には、チェックされていない例外が静的初期化ブロック1から伝播することを許可することもできます。しかし、これを意図的に行うのは本当に悪い考えです!問題は、JVM自体がチェックされていない例外をキャッチし、それをラップしてとして再スローすることExceptionInInitializerError
です。
注意:これはError
通常の例外ではありません。そこから回復しようとするべきではありません。
ほとんどの場合、例外はキャッチできません。
public class Test {
static {
int i = 1;
if (i == 1) {
throw new RuntimeException("Bang!");
}
}
public static void main(String[] args) {
try {
// stuff
} catch (Throwable ex) {
// This won't be executed.
System.out.println("Caught " + ex);
}
}
}
$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
at Test.<clinit>(Test.java:5)
try ... catch
上のExceptionInInitializerError
2をキャッチできる場所はありません。
場合によってはそれをキャッチすることができます。たとえばClass.forName(...)
、を呼び出してクラスの初期化をトリガーした場合、で呼び出しを囲みtry
、ExceptionInInitializerError
またはのいずれかをキャッチできますNoClassDefFoundError
。
ただし、回復を試みた場合ExceptionInInitializerError
、ロードブロッキングが発生する可能性があります。問題は、エラーをスローする前に、JVMが問題の原因となったクラスを「失敗」としてマークすることです。使用できなくなります。さらに、失敗したクラスに依存する他のクラスも、初期化しようとすると失敗状態になります。前進する唯一の方法は、失敗したクラスをすべてアンロードすることです。これは、動的にロードされるコード3で実現可能かもしれませんが、一般的には不可能です。
1-静的ブロックが無条件に未チェックの例外をスローする場合、これはコンパイルエラーです。
2- デフォルトのキャッチされない例外ハンドラーを登録することでそれをインターセプトできる可能性がありますが、「メイン」スレッドが開始できないため、回復できません。
3-失敗したクラスを回復したい場合、それらをロードしたクラスローダーを取り除く必要があります。
この設計決定の背後にある理由は何でしたか?
これは、処理できない例外をスローするコードをプログラマが作成しないようにするためです。
これまで見てきたように、静的イニシャライザの例外は、一般的なアプリケーションをレンガに変えます。言語設計者ができる最善のことは、チェックされたケースをコンパイルエラーとして処理することです。(残念ながら、チェックされていない例外に対してもこれを行うのは現実的ではありません。)
OK、コードが静的初期化子で例外をスローする必要がある場合はどうすればよいでしょうか。基本的に、2つの選択肢があります。
ブロック内の例外からの(完全!)回復が可能な場合は、それを実行します。
それ以外の場合は、静的初期化ブロック(または静的変数の初期化子)で初期化が行われないようにコードを再構成します。
Java言語仕様を見てください。静的なイニシャライザが失敗した 場合に、コンパイル時エラーであり、チェックされた例外で突然終了する可能性があると記載されています。
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
出力:Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: whathappenswhenastaticblockthrowsanexception at Main.<clinit>(Main.java:6) Caused by: java.lang.ClassNotFoundException: whathappen...
記述したコードは静的初期化ブロックを呼び出すことができないため、checkedをスローすることは役に立ちませんexceptions
。可能であれば、チェックされた例外がスローされたときにjvmは何をしますか?Runtimeexceptions
伝達されます。
例:SpringのDispatcherServlet(org.springframework.web.servlet.DispatcherServlet)は、チェックされた例外をキャッチし、別のチェックされていない例外をスローするシナリオを処理します。
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
チェックした例外をスローしてコンパイルすることもできます...
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}