1. Gambaran keseluruhan
Memeriksa keberadaan kelas dapat berguna ketika menentukan pelaksanaan antara muka yang akan digunakan. Teknik ini biasanya digunakan semasa penyediaan JDBC yang lebih lama.
Dalam tutorial ini, kita akan meneroka nuansa menggunakan Class.forName () untuk memeriksa keberadaan kelas di Java classpath .
2. Menggunakan Class.forName ()
Kita dapat memeriksa keberadaan kelas menggunakan Java Reflection, khususnya Class.forName () . Dokumentasi menunjukkan bahawa ClassNotFoundException akan dilemparkan jika kelas tidak dapat ditempatkan.
2.1. Bilakah Harapan ClassNotFoundException
Pertama, mari kita tulis ujian yang pastinya akan melancarkan ClassNotFoundException supaya kita dapat mengetahui bahawa ujian positif kita selamat:
@Test(expected = ClassNotFoundException.class) public void givenNonExistingClass_whenUsingForName_thenClassNotFound() throws ClassNotFoundException { Class.forName("class.that.does.not.exist"); }
Oleh itu, kami telah membuktikan bahawa kelas yang tidak wujud akan membuang ClassNotFoundException . Mari tulis ujian untuk kelas yang memang ada:
@Test public void givenExistingClass_whenUsingForName_thenNoException() throws ClassNotFoundException { Class.forName("java.lang.String"); }
Ujian ini membuktikan bahawa menjalankan Class.forName () dan tidak menangkap ClassNotFoundException adalah setara dengan kelas yang ditentukan yang ada di classpath . Walau bagaimanapun, ini bukanlah penyelesaian yang sempurna kerana kesan sampingan.
2.2. Kesan Sampingan: Permulaan Kelas
Penting untuk menunjukkan bahawa, tanpa menentukan pemuat kelas, Class.forName () harus menjalankan pemula statik pada kelas yang diminta . Ini boleh menyebabkan tingkah laku yang tidak dijangka.
Untuk mencontohkan tingkah laku ini, mari buat kelas yang melemparkan RuntimeException apabila blok pemula statiknya dijalankan agar kita dapat mengetahui dengan segera bila ia dijalankan:
public static class InitializingClass { static { if (true) { //enable throwing of an exception in a static initialization block throw new RuntimeException(); } } }
Kita dapat melihat dari dokumentasi forName () bahawa ia melemparkan ExceptionInInitializerError jika permulaan yang diprovokasi oleh kaedah ini gagal.
Mari tulis ujian yang akan menjangkakan Kesalahan ExceptionInInitializer semasa cuba mencari InitializingClass kami tanpa menentukan pemuat kelas:
@Test(expected = ExceptionInInitializerError.class) public void givenInitializingClass_whenUsingForName_thenInitializationError() throws ClassNotFoundException { Class.forName("path.to.InitializingClass"); }
Oleh kerana pelaksanaan blok inisialisasi statik kelas adalah kesan sampingan yang tidak dapat dilihat, kita sekarang dapat melihat bagaimana ia dapat menyebabkan masalah prestasi atau bahkan kesalahan. Mari lihat bagaimana untuk melangkau permulaan kelas.
3. Menceritakan Kelas.untukNama () untuk Melangkau Permulaan
Nasib baik bagi kami, terdapat kaedah forName () yang terlalu banyak , yang menerima pemuat kelas dan apakah inisialisasi kelas harus dijalankan.
Menurut dokumentasi, panggilan berikut adalah setara:
Class.forName("Foo") Class.forName("Foo", true, this.getClass().getClassLoader())
Dengan menukar true ke false , kita sekarang dapat menulis ujian yang memeriksa keberadaan InitializingClass kita tanpa mencetuskan blok inisialisasi statiknya :
@Test public void givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException() throws ClassNotFoundException { Class.forName("path.to.InitializingClass", false, getClass().getClassLoader()); }
4. Java 9 Modul
Untuk projek-projek Java 9+, ada beban ketiga Class.forName () , yang menerima satu Modul dan String nama kelas. Beban berlebihan ini tidak menjalankan pemula kelas secara lalai. Juga, terutamanya, ia akan menjadi kosong apabila kelas yang diminta tidak wujud daripada membuang ClassNotFoundException .
5. Kesimpulan
Dalam tutorial ringkas ini, kami telah mendedahkan kesan sampingan dari inisialisasi kelas ketika menggunakan Class.forName () dan mendapati bahawa anda boleh menggunakan forName () overload untuk mengelakkan perkara itu terjadi.
Kod sumber dengan semua contoh dalam tutorial ini boleh didapati di GitHub.