Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까? 이 디자인 결정의 이유는 무엇입니까?
Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까? 이 디자인 결정의 이유는 무엇입니까?
답변:
소스에서 이러한 확인 된 예외를 처리 할 수 없기 때문입니다. 초기화 프로세스를 제어 할 수 없으며 try-catch로 둘러 쌀 수 있도록 소스에서 static {} 블록을 호출 할 수 없습니다.
점검 된 예외로 표시된 오류를 처리 할 수 없으므로 점검 된 예외 정적 블록을 던지는 것을 허용하지 않기로 결정했습니다.
정적 블록은 검사 된 예외를 발생 시키지 않아야 하지만 여전히 검사되지 않은 / 런타임 예외가 발생하도록 허용합니다. 그러나 위의 이유에 따르면 이들 중 어느 것도 처리 할 수 없습니다.
요약하면,이 제한은 개발자가 응용 프로그램을 복구 할 수없는 오류를 초래할 수있는 무언가를 작성하지 못하게합니다 (또는 적어도 어렵게 만듭니다).
static { if(1 < 10) { throw new NullPointerException(); } }
점검 된 예외를 발견하고이를 점검되지 않은 예외로 다시 던져서 문제점을 해결할 수 있습니다. 이 확인되지 않은 예외 클래스는 래퍼로 잘 작동합니다 java.lang.ExceptionInInitializerError
.
샘플 코드 :
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) {
대신 보십시오 .
System.exit(...)
(또는 이에 상응하는) 유일한 옵션입니다
다음과 같이 보일 것입니다 (이것은 유효한 Java 코드 가 아닙니다 )
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
그러나 당신이 그것을 잡는 위치에 광고하는 방법? 확인 된 예외는 catch가 필요합니다. 클래스를 초기화 할 수있는 (또는 이미 초기화 되었기 때문에 그렇지 않을 수도있는) 예제를 상상해 보시고 복잡성을 주목하기 위해 다른 정적 이니셜 라이저에 예제를 넣습니다.
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?
}
}
그리고 또 다른 불쾌한 일-
interface MyInterface {
final static ClassA a = new ClassA();
}
ClassA에 확인 된 예외가 발생하는 정적 초기화 프로그램이 있다고 상상해보십시오.이 경우 MyInterface ( '숨겨진'정적 초기화 프로그램이있는 인터페이스)는 예외를 처리하거나 처리해야합니다. 인터페이스에서 예외 처리? 그대로 두는 것이 좋습니다.
main
확인 된 예외를 던질 수 있습니다. 분명히 그것들은 처리 할 수 없습니다.
main()
스택 추적을 사용하여 예외를 인쇄하는 실행중인 스레드에 "매직"(기본) Thread.UncaughtExceptionHandler가 있다고 가정하고 System.err
를 호출합니다 System.exit()
. 결국이 질문에 대한 대답은 아마도 "Java 디자이너가 그렇게 말했기 때문"일 것입니다.
Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까?
기술적으로는이 작업을 수행 할 수 있습니다. 그러나 검사 된 예외는 블록 내에서 발견되어야합니다. 확인 된 예외는 블록 밖으로 전파 될 수 없습니다 .
기술적으로, 검사되지 않은 예외가 정적 초기화 블록 ( 1) 으로부터 전파되도록하는 것도 가능하다 . 그러나 이것을 의도적으로 수행하는 것은 정말 나쁜 생각입니다! 문제는 JVM 자체가 확인되지 않은 예외를 잡아서 랩핑하여 다시로 던진다는 것 ExceptionInInitializerError
입니다.
주의 : 그것은 Error
일반적인 예외 는 아닙니다. 복구를 시도해서는 안됩니다.
대부분의 경우 예외를 포착 할 수 없습니다.
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)
try ... catch
위에서 ExceptionInInitializerError
2 를 붙잡을 수있는 곳은 없습니다 .
어떤 경우에는 그것을 잡을 수 있습니다. 예를 들어,을 호출하여 클래스 초기화를 트리거 한 경우 Class.forName(...)
호출을에 묶고 a 또는 그 이후를 try
잡을 수 있습니다 .ExceptionInInitializerError
NoClassDefFoundError
당신이 시도 할 경우, 복구 에서 ExceptionInInitializerError
당신은 장애물로 실행되기 쉽다. 문제는 오류를 발생시키기 전에 JVM이 문제를 일으킨 클래스를 "실패"로 표시한다는 것입니다. 당신은 단순히 그것을 사용할 수 없습니다. 또한 실패한 클래스에 의존하는 다른 클래스도 초기화하려고하면 실패한 상태가됩니다. 앞으로 나아가는 유일한 방법은 모든 실패한 클래스를 언로드하는 것입니다. 즉 수 동적으로로드 된 코드에 대한 실현 가능한 3 하지만, 일반적으로는 없습니다.
1-정적 블록 이 검사되지 않은 예외를 무조건 발생시키는 경우 컴파일 오류 입니다.
2 - 당신은 수있는 기본 캐치되지 않는 예외 핸들러를 등록하여 차단을 할 수있을, 그러나 그것은 당신의 "주"스레드를 시작할 수 없습니다 때문에, 복구 할 수 없습니다.
3-실패한 클래스를 복구하려면 해당 클래스를로드 한 클래스 로더를 제거해야합니다.
이 디자인 결정의 이유는 무엇입니까?
처리 할 수없는 예외를 발생시키는 코드를 작성 하지 않도록 프로그래머를 보호합니다 !
앞에서 본 것처럼 정적 초기화 프로그램의 예외는 일반적인 응용 프로그램을 브릭으로 바꿉니다. 언어 디자이너가 할 수있는 최선의 방법은 체크 된 사례를 컴파일 오류로 처리하는 것입니다. (불행히도, 확인되지 않은 예외에 대해서도이를 수행하는 것은 실용적이지 않습니다.)
코드가 정적 초기화 프로그램에서 예외를 발생시키기 위해 "필요한"경우 어떻게해야합니까? 기본적으로 두 가지 대안이 있습니다.
블록 내 예외에서 (full!) 복구가 가능하면 그렇게하십시오.
그렇지 않으면 정적 초기화 블록 (또는 정적 변수의 초기화 자)에서 초기화가 발생하지 않도록 코드를 재구성하십시오.
Java 언어 사양을 살펴보십시오. 정적 초기화 프로그램 이 실패하면 확인 된 예외로 갑자기 완료 될 수있는 경우 컴파일 시간 오류라고 명시되어 있습니다 .
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
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...
작성한 코드는 정적 초기화 블록을 호출 할 수 없으므로 checked를 throw하는 것은 유용하지 않습니다 exceptions
. 가능하다면 확인 된 예외가 발생했을 때 jvm은 어떻게합니까? Runtimeexceptions
전파됩니다.
예를 들면 다음과 같습니다. Spring의 DispatcherServlet (org.springframework.web.servlet.DispatcherServlet)은 확인 된 예외를 포착하고 확인되지 않은 다른 예외를 발생시키는 시나리오를 처리합니다.
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());
}
확인 된 예외도 throw하여 컴파일 할 수 있습니다 ....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}