Bilakah Java Membuang Kesalahan ExceptionInInitializer?

1. Gambaran keseluruhan

Dalam tutorial ringkas ini, kita akan melihat apa yang menyebabkan Java membuang contoh pengecualian ExceptionInInitializerError .

Kita akan mulakan dengan sedikit teori. Kemudian kita akan melihat beberapa contoh pengecualian ini dalam praktiknya.

2. Kesalahan ExceptionInInitializer

The ExceptionInInitializerError menunjukkan bahawa pengecualian tidak dijangka telah berlaku dalam pengasal statik. Pada asasnya, apabila kita melihat pengecualian ini, kita harus tahu bahawa Java gagal menilai blok pemula statik atau membuat pemboleh ubah statik.

Sebenarnya, setiap kali ada pengecualian yang berlaku di dalam penginisialisasi statis, Java secara automatik membungkus pengecualian tersebut di dalam kelas ExceptionInInitializerError . Dengan cara ini, ia juga merujuk kepada pengecualian sebenar sebagai punca.

Setelah kita mengetahui alasan di sebalik pengecualian ini, mari kita lihat dalam praktiknya.

3. Blok Permulaan Statik

Untuk mempunyai inisialisasi blok statik yang gagal, kita akan membahagi bilangan bulat dengan sifar dengan sengaja:

public class StaticBlock { private static int state; static { state = 42 / 0; } }

Sekarang jika kita mencetuskan permulaan kelas dengan sesuatu seperti:

new StaticBlock();

Kemudian, kita akan melihat pengecualian berikut:

java.lang.ExceptionInInitializerError at com.baeldung...(ExceptionInInitializerErrorUnitTest.java:18) Caused by: java.lang.ArithmeticException: / by zero at com.baeldung.StaticBlock.(ExceptionInInitializerErrorUnitTest.java:35) ... 23 more

Seperti yang telah disebutkan sebelumnya, Java membuang pengecualian ExceptionInInitializerError sambil mempertahankan referensi ke akar penyebabnya:

assertThatThrownBy(StaticBlock::new) .isInstanceOf(ExceptionInInitializerError.class) .hasCauseInstanceOf(ArithmeticException.class);

Perlu disebut juga bahawa kaedah adalah kaedah inisialisasi kelas dalam JVM.

4. Permulaan Pembolehubah Statik

Perkara yang sama berlaku jika Java gagal memulakan pemboleh ubah statik:

public class StaticVar { private static int state = initializeState(); private static int initializeState() { throw new RuntimeException(); } }

Sekali lagi, jika kita mencetuskan proses inisialisasi kelas:

new StaticVar();

Maka pengecualian yang sama berlaku:

java.lang.ExceptionInInitializerError at com.baeldung...(ExceptionInInitializerErrorUnitTest.java:11) Caused by: java.lang.RuntimeException at com.baeldung.StaticVar.initializeState(ExceptionInInitializerErrorUnitTest.java:26) at com.baeldung.StaticVar.(ExceptionInInitializerErrorUnitTest.java:23) ... 23 more

Mirip dengan blok pemula statik, punca pengecualian juga terpelihara:

assertThatThrownBy(StaticVar::new) .isInstanceOf(ExceptionInInitializerError.class) .hasCauseInstanceOf(RuntimeException.class);

5. Pengecualian yang diperiksa

Sebagai sebahagian dari spesifikasi bahasa Java (JLS-11.2.3), kami tidak dapat membuang pengecualian yang dicentang di dalam blok penginisial statis atau pembentuk pemboleh ubah statik. Contohnya, jika kita berusaha melakukannya:

public class NoChecked { static { throw new Exception(); } }

Penyusun akan gagal dengan ralat kompilasi berikut:

java: initializer must be able to complete normally

Sebagai konvensyen, kita harus membungkus kemungkinan pengecualian yang diperiksa di dalam contoh ExceptionInInitializerError apabila logik inisialisasi statik kita membuang pengecualian yang diperiksa:

public class CheckedConvention { private static Constructor constructor; static { try { constructor = CheckedConvention.class.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new ExceptionInInitializerError(e); } } }

Seperti yang ditunjukkan di atas, kaedah getDeclaredConstructor () membuang pengecualian yang diperiksa. Oleh itu, kami memperoleh pengecualian yang diperiksa dan membungkusnya seperti yang dicadangkan oleh konvensyen.

Oleh kerana kami sudah mengembalikan contoh pengecualian ExceptionInInitializerError secara eksplisit, Java tidak akan membungkus pengecualian ini di dalam contoh ExceptionInInitializerError yang lain .

Namun, jika kita membuang pengecualian lain yang tidak dicentang, Java akan membuang ExceptionInInitializerError yang lain :

static { try { constructor = CheckedConvention.class.getConstructor(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }

Di sini, kami membungkus pengecualian yang dicentang di dalam yang tidak dicentang. Kerana pengecualian yang tidak dicentang ini bukan merupakan contoh ExceptionInInitializerError, Java akan membungkusnya lagi, menghasilkan jejak tumpukan yang tidak dijangka ini:

java.lang.ExceptionInInitializerError at com.baeldung.exceptionininitializererror... Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodException: ... Caused by: java.lang.NoSuchMethodException: com.baeldung.CheckedConvention.() at java.base/java.lang.Class.getConstructor0(Class.java:3427) at java.base/java.lang.Class.getConstructor(Class.java:2165)

Seperti yang ditunjukkan di atas, jika kita mengikuti konvensyen, maka jejak timbunan akan jauh lebih bersih daripada ini.

5.1. BukaJDK

Baru-baru ini, konvensyen ini bahkan digunakan dalam kod sumber OpenJDK itu sendiri. Contohnya, inilah cara AtomicReference menggunakan pendekatan ini:

public class AtomicReference implements java.io.Serializable { private static final VarHandle VALUE; static { try { MethodHandles.Lookup l = MethodHandles.lookup(); VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } } private volatile V value; // omitted }

6. Kesimpulannya

Dalam tutorial ini, kami melihat apa yang menyebabkan Java membuang contoh pengecualian ExceptionInInitializerError .

Seperti biasa, semua contoh boleh didapati di GitHub.