Java neden statik başlatma bloğundan işaretli bir istisna atmaya izin vermiyor? Bu tasarım kararının ardındaki neden neydi?
Java neden statik başlatma bloğundan işaretli bir istisna atmaya izin vermiyor? Bu tasarım kararının ardındaki neden neydi?
Yanıtlar:
Çünkü kaynağınızdaki bu işaretli istisnaları ele almak mümkün değildir. Başlatma işlemi üzerinde herhangi bir kontrole sahip değilsiniz ve statik {} blokları kaynağınızdan çağrılamaz, böylece bunları try-catch ile çevreleyebilirsiniz.
İşaretli bir istisna tarafından belirtilen herhangi bir hatayı işleyemediğiniz için, denetlenen istisnaların statik bloklarının atılmasına izin verilmemesine karar verildi.
Statik blok, kontrol edilen istisnaları atmamalıdır, ancak yine de işaretlenmemiş / çalışma zamanı istisnalarının atılmasına izin verir. Ancak yukarıdaki nedenlere göre bunları da ele alamazsınız.
Özetlemek gerekirse, bu kısıtlama geliştiricinin uygulamanın kurtaramayacağı hatalara yol açabilecek bir şey oluşturmasını engeller (veya en azından zorlaştırır).
static { if(1 < 10) { throw new NullPointerException(); } }
İşaretli herhangi bir özel durumu yakalayıp denetlenmeyen bir özel durum olarak yeniden karşılaştırarak soruna geçici bir çözüm bulabilirsiniz. Bu kontrolsüz istisna sınıfı bir sarmalayıcı iyi çalışır: java.lang.ExceptionInInitializerError
.
Basit kod:
protected static class _YieldCurveConfigHelperSingleton {
public static YieldCurveConfigHelper _staticInstance;
static {
try {
_staticInstance = new YieldCurveConfigHelper();
}
catch (IOException | SAXException | JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
}
catch (Exception e) {
yerine.
System.exit(...)
(veya eşdeğeri) tek seçeneğinizdir
Bunun gibi görünmesi gerekir (bu geçerli bir Java kodu değildir )
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
ama reklamı nereden yakalarsınız? İşaretli istisnalar yakalamak gerektirir. Sınıfı başlatabilecek bazı örnekleri düşünün (veya zaten başlatıldığı için olmayabilir) ve sadece getireceği karmaşıklığın dikkatini çekmek için, örnekleri başka bir statik initalizer'e koydum:
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?
}
}
Ve başka bir kötü şey -
interface MyInterface {
final static ClassA a = new ClassA();
}
ClassA'nın kontrol edilen bir istisna atan statik bir başlatıcısı olduğunu düşünün: Bu durumda MyInterface ('gizli' statik başlatıcısı olan bir arayüz) istisnayı atmak veya işlemek zorunda kalır - bir arayüzde istisna işleme? Olduğu gibi bıraksan iyi olur.
main
işaretli istisnalar atabilir. Açıkçası bunlar ele alınamaz.
main()
yığın izleme ile istisna yazdırır System.err
, sonra çağırır iş parçacığına bağlı bir "büyülü" (varsayılan) Thread.UncaughtExceptionHandler olduğunu varsayalım System.exit()
. Sonunda, bu sorunun cevabı muhtemelen: "çünkü Java tasarımcıları böyle söyledi".
Java neden statik başlatma bloğundan işaretli bir istisna atmaya izin vermiyor?
Teknik olarak bunu yapabilirsiniz. Ancak, denetlenen kural dışı durum blok içinde yakalanmalıdır. İşaretli bir istisnanın bloktan yayılmasına izin verilmez .
Teknik olarak, kontrolsüz bir istisnanın statik bir başlatıcı bloğundan yayılması da mümkündür 1 . Ama bunu kasten yapmak gerçekten kötü bir fikir! Sorun, JVM'nin kendisinin kontrol edilmeyen istisnayı yakalaması ve onu sararak bir ExceptionInInitializerError
.
Not: bu bir Error
normal bir istisna değildir. Ondan kurtulmaya çalışmamalısınız.
Çoğu durumda, istisna yakalanamaz:
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)
2'yitry ... catch
yakalamak için yukarıdakilere yerleştirebileceğiniz hiçbir yer yoktur.ExceptionInInitializerError
.
Bazı durumlarda yakalayabilirsiniz. Eğer arayarak sınıf başlatma tetiklenen Örneğin, Class.forName(...)
, bir aramayı çevreleyebileceğim try
catch ve ya ExceptionInInitializerError
veya sonrakiNoClassDefFoundError
.
Eğer denerseniz Ancak, kurtarmak bir gelen ExceptionInInitializerError
bir yolda engelle yükümlüdür. Sorun, hata atmadan önce JVM soruna neden olan sınıfı "başarısız" olarak işaretler. Sadece kullanamazsınız. Ayrıca, başarısız sınıfa bağımlı diğer sınıflar da başlatma girişiminde bulunurlarsa başarısız olurlar. İlerlemenin tek yolu tüm başarısız sınıfları boşaltmaktır. Bu , dinamik olarak yüklenmiş kod 3 için uygun olabilir , ancak genel olarak değildir.
1 - Statik bir blok koşulsuz olarak kontrol edilmeyen bir istisna atarsa bir derleme hatasıdır .
2 - Sen belki varsayılan bir yakalanmamış istisna işleyicisi kaydederek kesişmesine bunu yapabilmek, ama bu "ana" parçacığı başlatılamıyor çünkü, kurtarmak için izin vermeyecektir.
3 - Başarısız sınıfları kurtarmak isterseniz, onları yükleyen sınıf yükleyiciden kurtulmanız gerekir.
Bu tasarım kararının ardındaki neden neydi?
Programcıyı yapamayan istisnalar atan kod yazmaya karşı korumaktır ele !
Gördüğümüz gibi, statik bir başlatıcıdaki bir istisna tipik bir uygulamayı tuğlaya dönüştürür. Dil tasarımcılarının yapabileceği en iyi şey, kontrol edilen durumla bir derleme hatası olarak ilgilenmektir. (Maalesef, kontrol edilmeyen istisnalar için de bunu yapmak pratik değildir.)
Tamam, bu yüzden kodunuz statik bir başlatıcıda istisnalar atmaya "ihtiyaç duyuyorsa" ne yapmalısınız? Temel olarak, iki alternatif vardır:
Blok içindeki istisnadan (tam!) Kurtarma mümkün ise, bunu yapın.
Aksi takdirde, kodunuzu, başlatma işlemini statik başlatma bloğunda (veya statik değişkenlerin başlatmalarında) gerçekleşmeyecek şekilde yeniden yapılandırın.
Java Dil Spesifikasyonlarına bir göz atın : statik başlatıcı başarısız olursa , derlenmiş bir istisna ile aniden tamamlanabiliyorsa, derleme zamanı hatası olduğu belirtilir .
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
Çıktı: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...
Yazdığınız hiçbir kod statik başlatma bloğunu çağıramadığından, işaretli atmak yararlı değildir exceptions
. Mümkünse, kontrol edilen istisnalar atıldığında jvm ne yapardı? Runtimeexceptions
yayılır.
Örneğin: Spring's DispatcherServlet (org.springframework.web.servlet.DispatcherServlet), denetlenen bir özel durumu yakalayan ve başka bir denetlenmeyen özel durum atan senaryoyu işler.
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());
}
Ayrıca kontrol edilen bir İstisna atma derlemek mümkün ....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}