1. Pengenalan
Kedua ClassNotFoundException dan NoClassDefFoundError berlaku apabila JVM tidak dapat mencari kelas yang diminta di classpath. Walaupun kelihatan biasa, terdapat beberapa perbezaan inti antara keduanya.
Dalam tutorial ini, kita akan membincangkan beberapa sebab kejadiannya dan penyelesaiannya.
2. ClassNotFoundException
ClassNotFoundException adalah pengecualian yang dicentang yang berlaku apabila aplikasi cuba memuat kelas melalui nama yang memenuhi syarat sepenuhnya dan tidak dapat menemui definisinya di classpath.
Ini berlaku terutamanya ketika cuba memuat kelas menggunakan Class.forName () , ClassLoader.loadClass () atau ClassLoader.findSystemClass () . Oleh itu, kita perlu berhati-hati dengan java.lang.ClassNotFoundException semasa bekerja dengan refleksi.
Sebagai contoh, mari kita cuba memuatkan kelas pemacu JDBC tanpa menambahkan kebergantungan yang diperlukan yang akan menjadikan kita ClassNotFoundException:
@Test(expected = ClassNotFoundException.class) public void givenNoDrivers_whenLoadDriverClass_thenClassNotFoundException() throws ClassNotFoundException { Class.forName("oracle.jdbc.driver.OracleDriver"); }
3. NoClassDefFoundError
NoClassDefFoundError adalah kesalahan yang boleh membawa maut. Ia berlaku apabila JVM tidak dapat mencari definisi kelas semasa cuba:
- Buat kelas dengan menggunakan kata kunci baru
- Muatkan kelas dengan panggilan kaedah
Kesalahan berlaku apabila pengkompil berjaya menyusun kelas, tetapi waktu jalan Java tidak dapat mencari fail kelas. Ia biasanya berlaku apabila terdapat pengecualian ketika menjalankan blok statik atau memulakan medan statik kelas, jadi inisialisasi kelas gagal.
Mari kita fikirkan senario yang merupakan salah satu cara mudah untuk menghasilkan semula masalah tersebut. Permulaan ClassWithInitErrors menimbulkan pengecualian. Oleh itu, apabila kita cuba membuat objek ClassWithInitErrors, ia membuang ExceptionInInitializerError.
Sekiranya kita cuba memuatkan kelas yang sama sekali lagi, kita mendapat NoClassDefFoundError:
public class ClassWithInitErrors { static int data = 1 / 0; }
public class NoClassDefFoundErrorExample { public ClassWithInitErrors getClassWithInitErrors() { ClassWithInitErrors test; try { test = new ClassWithInitErrors(); } catch (Throwable t) { System.out.println(t); } test = new ClassWithInitErrors(); return test; } }
Mari kita tulis kes ujian untuk senario ini:
@Test(expected = NoClassDefFoundError.class) public void givenInitErrorInClass_whenloadClass_thenNoClassDefFoundError() { NoClassDefFoundErrorExample sample = new NoClassDefFoundErrorExample(); sample.getClassWithInitErrors(); }
4. Ketetapan
Kadang-kadang, memerlukan masa yang lama untuk mendiagnosis dan menyelesaikan dua masalah ini. Sebab utama kedua-dua masalah tersebut adalah ketiadaan fail kelas (di classpath) pada waktu runtime.
Mari kita lihat beberapa pendekatan yang boleh kita pertimbangkan ketika menangani salah satu daripada ini:
- Kita perlu memastikan sama ada kelas atau balang yang mengandungi kelas itu ada di classpath. Sekiranya tidak, kita perlu menambahkannya
- Sekiranya ia tersedia di classpath aplikasi maka kemungkinan classpath akan diganti. Untuk memperbaikinya, kita perlu mencari jalan kelas yang tepat yang digunakan oleh aplikasi kita
- Juga, jika aplikasi menggunakan pemuat kelas berganda, kelas yang dimuat oleh satu pemuat kelas mungkin tidak tersedia oleh pemuat kelas lain. Untuk menyelesaikannya dengan baik, penting untuk mengetahui bagaimana pemuat kelas bekerja di Java
5. Ringkasan
Walaupun kedua-dua pengecualian ini berkaitan dengan classpath dan Java runtime tidak dapat mencari kelas pada waktu run, penting untuk diperhatikan perbezaannya.
Runtime Java melemparkan ClassNotFoundException semasa cuba memuat kelas pada waktu runtime sahaja dan namanya diberikan semasa runtime. Dalam kes NoClassDefFoundError, kelas hadir pada waktu kompilasi, tetapi runtime Java tidak dapat menemuinya di Java classpath semasa runtime.
Seperti biasa, kod lengkap untuk semua contoh boleh didapati di GitHub.