Kapan Java Melempar ExceptionInInitializerError?

1. Ikhtisar

Dalam tutorial singkat ini, kita akan melihat apa yang menyebabkan Java menampilkan sebuah instance dari pengecualian ExceptionInInitializerError .

Kami akan mulai dengan sedikit teori. Kemudian kita akan melihat beberapa contoh pengecualian ini dalam praktiknya.

2. ExceptionInitializerError

The ExceptionInInitializerError menunjukkan bahwa pengecualian yang tak terduga telah terjadi dalam initializer statis. Pada dasarnya, ketika kita melihat pengecualian ini, kita harus tahu bahwa Java gagal mengevaluasi blok penginisialisasi statis atau untuk membuat instance variabel statis.

Faktanya, setiap kali pengecualian terjadi di dalam penginisialisasi statis, Java secara otomatis membungkus pengecualian itu di dalam instance kelas ExceptionInInitializerError . Dengan cara ini, ia juga mempertahankan referensi ke pengecualian sebenarnya sebagai akar masalah.

Sekarang setelah kita mengetahui alasan di balik pengecualian ini, mari kita lihat dalam praktiknya.

3. Blok Penginisialisasi Statis

Untuk memiliki penginisialisasi blok statis yang gagal, kami akan membagi integer dengan nol dengan sengaja:

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

Sekarang jika kita memicu inisialisasi 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 disebutkan sebelumnya, Java memunculkan pengecualian ExceptionInInitializerError sambil mempertahankan referensi ke akar penyebab:

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

Perlu juga disebutkan bahwa metode adalah metode inisialisasi kelas di JVM.

4. Inisialisasi Variabel Statis

Hal yang sama terjadi jika Java gagal menginisialisasi variabel statis:

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

Sekali lagi, jika kita memicu proses inisialisasi kelas:

new StaticVar();

Kemudian pengecualian yang sama terjadi:

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 penginisialisasi statis, akar penyebab pengecualian juga dipertahankan:

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

5. Pengecualian yang Diperiksa

Sebagai bagian dari spesifikasi bahasa Java (JLS-11.2.3), kami tidak dapat menampilkan pengecualian yang dicentang di dalam blok penginisialisasi statis atau penginisialisasi variabel statis. Misalnya, jika kami mencoba melakukannya:

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

Kompilator akan gagal dengan kesalahan kompilasi berikut:

java: initializer must be able to complete normally

Sebagai konvensi, kita harus membungkus kemungkinan pengecualian yang diperiksa di dalam sebuah instance ExceptionInInitializerError ketika logika inisialisasi statis kita menampilkan pengecualian yang dicentang:

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, metode getDeclaredConstructor () memunculkan pengecualian yang dicentang. Oleh karena itu, kami menangkap pengecualian yang dicentang dan membungkusnya seperti yang disarankan konvensi.

Karena kita sudah mengembalikan sebuah contoh pengecualian ExceptionInInitializerError secara eksplisit, Java tidak akan membungkus pengecualian ini di dalam contoh ExceptionInInitializerError lainnya .

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

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. Karena pengecualian yang tidak dicentang ini bukan instance ExceptionInInitializerError, Java akan membungkusnya lagi, menghasilkan pelacakan tumpukan yang tidak terduga 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 konvensi, maka pelacakan tumpukan akan jauh lebih bersih dari ini.

5.1. OpenJDK

Baru-baru ini, konvensi ini bahkan digunakan dalam kode sumber OpenJDK itu sendiri. Misalnya, berikut ini 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. Kesimpulan

Dalam tutorial ini, kita melihat apa yang menyebabkan Java memunculkan instance pengecualian ExceptionInInitializerError .

Seperti biasa, semua contoh tersedia di GitHub.