Mengapa Java tidak mengizinkan untuk menampilkan pengecualian yang dicentang dari blok inisialisasi statis? Apa alasan di balik keputusan desain ini?
java
exception
static-initializer
missingfaktor
sumber
sumber
Jawaban:
Karena tidak mungkin menangani pengecualian yang dicentang ini di sumber Anda. Anda tidak memiliki kendali apa pun atas proses inisialisasi dan blok {} statis tidak dapat dipanggil dari sumber Anda sehingga Anda bisa mengelilinginya dengan try-catch.
Karena Anda tidak dapat menangani kesalahan apa pun yang ditunjukkan oleh pengecualian yang dicentang, maka diputuskan untuk tidak mengizinkan pelemparan blok statis pengecualian yang diperiksa.
Blok statis tidak boleh menampilkan pengecualian yang dicentang tetapi masih memungkinkan pengecualian yang tidak dicentang / runtime-pengecualian untuk dilemparkan. Tetapi menurut alasan di atas Anda juga tidak dapat menangani ini.
Untuk meringkas, pembatasan ini mencegah (atau setidaknya mempersulit) pengembang untuk membuat sesuatu yang dapat mengakibatkan kesalahan yang tidak dapat dipulihkan oleh aplikasi.
sumber
static { if(1 < 10) { throw new NullPointerException(); } }
Anda dapat mengatasi masalah ini dengan menangkap setiap pengecualian yang dicentang dan menampilkannya kembali sebagai pengecualian yang tidak dicentang. Kelas pengecualian dicentang ini bekerja dengan baik sebagai pembungkus:
java.lang.ExceptionInInitializerError
.Kode sampel:
protected static class _YieldCurveConfigHelperSingleton { public static YieldCurveConfigHelper _staticInstance; static { try { _staticInstance = new YieldCurveConfigHelper(); } catch (IOException | SAXException | JAXBException e) { throw new ExceptionInInitializerError(e); } } }
sumber
catch (Exception e) {
sebagai gantinya.System.exit(...)
(atau setara) adalah satu-satunya pilihan Anda,Ini harus terlihat seperti ini (ini bukan kode Java yang valid)
// Not a valid Java Code static throws SomeCheckedException { throw new SomeCheckedException(); }
tapi bagaimana iklan di mana Anda menangkapnya? Pengecualian yang dicentang perlu ditangkap. Bayangkan beberapa contoh yang mungkin menginisialisasi kelas (atau mungkin tidak karena sudah diinisialisasi), dan hanya untuk menarik perhatian dari kerumitan yang akan diperkenalkan, saya meletakkan contoh di initalizer statis lain:
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? } }
Dan hal buruk lainnya -
interface MyInterface { final static ClassA a = new ClassA(); }
Bayangkan ClassA memiliki penginisialisasi statis yang melontarkan pengecualian yang dicentang: Dalam hal ini MyInterface (yang merupakan antarmuka dengan penginisialisasi statis 'tersembunyi') harus membuang pengecualian atau menanganinya - penanganan pengecualian pada antarmuka? Lebih baik biarkan apa adanya.
sumber
main
bisa melempar pengecualian yang dicentang. Jelas itu tidak bisa ditangani.main()
yang mencetak pengecualian dengan pelacakan tumpukan keSystem.err
, lalu memanggilSystem.exit()
. Pada akhirnya, jawaban untuk pertanyaan ini mungkin: "karena desainer Java berkata demikian".Secara teknis, Anda bisa melakukan ini. Namun, pengecualian yang dicentang harus ditangkap di dalam blok. Pengecualian yang dicentang tidak diperbolehkan untuk menyebar keluar dari blok.
Secara teknis, dimungkinkan juga untuk mengizinkan pengecualian yang tidak dicentang untuk menyebar keluar dari blok penginisialisasi statis 1 . Tapi itu ide yang sangat buruk untuk melakukan ini dengan sengaja! Masalahnya adalah bahwa JVM sendiri menangkap pengecualian yang tidak dicentang, dan membungkusnya dan menampilkannya kembali sebagai file
ExceptionInInitializerError
.NB: itu
Error
bukan pengecualian biasa. Anda tidak boleh mencoba memulihkannya.Dalam kebanyakan kasus, pengecualian tidak dapat ditangkap:
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)
Tidak ada tempat di mana Anda dapat menempatkan a
try ... catch
di atas untuk menangkapExceptionInInitializerError
2 .Dalam beberapa kasus, Anda bisa tertular. Misalnya, jika Anda memicu inisialisasi kelas dengan memanggil
Class.forName(...)
, Anda bisa mengapit panggilan dalam atry
dan menangkapExceptionInInitializerError
atau berikutnyaNoClassDefFoundError
.Namun, jika Anda mencoba memulihkan dari suatu
ExceptionInInitializerError
Anda kemungkinan besar akan mengalami hambatan. Masalahnya adalah sebelum melakukan kesalahan, JVM menandai kelas yang menyebabkan masalah sebagai "gagal". Anda tidak akan bisa menggunakannya. Selain itu, kelas lain yang bergantung pada kelas yang gagal juga akan masuk ke status gagal jika mereka mencoba untuk menginisialisasi. Satu-satunya cara untuk meneruskan adalah membongkar semua kelas yang gagal. Itu mungkin layak untuk kode 3 yang dimuat secara dinamis , tetapi secara umum tidak.1 - Ini adalah kesalahan kompilasi jika blok statis tanpa syarat menampilkan pengecualian yang tidak dicentang.
2 - Anda mungkin dapat mencegatnya dengan mendaftarkan pengendali pengecualian tidak tertangkap default, tetapi itu tidak memungkinkan Anda untuk memulihkan, karena utas "utama" Anda tidak dapat dimulai.
3 - Jika Anda ingin memulihkan kelas yang gagal, Anda harus membuang classloader yang memuatnya.
Ini untuk melindungi programmer dari menulis kode yang melempar pengecualian yang tidak dapat ditangani!
Seperti yang telah kita lihat, pengecualian dalam penginisialisasi statis mengubah aplikasi biasa menjadi batu bata. Hal terbaik yang dapat dilakukan oleh desainer bahasa adalah menangani kasus yang dicentang sebagai kesalahan kompilasi. (Sayangnya, tidak praktis melakukan ini untuk pengecualian yang tidak dicentang juga.)
Oke, jadi apa yang harus Anda lakukan jika kode "perlu" menampilkan pengecualian dalam penginisialisasi statis. Pada dasarnya ada dua alternatif:
Jika pemulihan (penuh!) Dari pengecualian dalam blok dimungkinkan, maka lakukan itu.
Jika tidak, atur ulang kode Anda sehingga inisialisasi tidak terjadi dalam blok inisialisasi statis (atau dalam penginisialisasi variabel statis).
sumber
Lihatlah Spesifikasi Bahasa Java : dinyatakan bahwa ini adalah kesalahan waktu kompilasi jika penginisialisasi statis
gagaldapat diselesaikan secara tiba - tiba dengan pengecualian yang dicentang.sumber
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Output: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...
Karena tidak ada kode yang Anda tulis yang dapat memanggil blok inisialisasi statis, tidak berguna untuk membuang centang
exceptions
. Jika memungkinkan, apa yang akan dilakukan jvm ketika pengecualian yang dicentang dilemparkan?Runtimeexceptions
disebarkan.sumber
Misalnya: Spring's DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) menangani skenario yang menangkap pengecualian yang dicentang dan menampilkan pengecualian lain yang tidak dicentang.
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()); }
sumber
Saya dapat mengkompilasi melempar Exception juga ....
static { try { throw new IOException(); } catch (Exception e) { // Do Something } }
sumber