Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까?
Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까? 이 디자인 결정의 이유는 무엇입니까?
소스에서 이러한 확인 된 예외를 처리 할 수 없기 때문입니다. 초기화 프로세스를 제어 할 수 없으며 try-catch로 둘러 쌀 수 있도록 소스에서 static {} 블록을 호출 할 수 없습니다.
점검 된 예외로 표시된 오류를 처리 할 수 없으므로 점검 된 예외 정적 블록을 던지는 것을 허용하지 않기로 결정했습니다.
정적 블록은 검사 된 예외를 발생 시키지 않아야 하지만 여전히 검사되지 않은 / 런타임 예외가 발생하도록 허용합니다. 그러나 위의 이유에 따르면 이들 중 하나를 처리 할 수 없습니다.
요약하면,이 제한은 개발자가 응용 프로그램을 복구 할 수없는 오류를 초래할 수있는 무언가를 작성하지 못하게합니다 (또는 적어도 어렵게 만듭니다).
점검 된 예외를 발견하고이를 점검되지 않은 예외로 다시 던져서 문제점을 해결할 수 있습니다. 이 확인되지 않은 예외 클래스는 래퍼로 잘 작동합니다 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);
}
}
}
다음과 같이 보일 것입니다 (이것은 유효한 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 ( '숨겨진'정적 초기화 프로그램이있는 인터페이스)는 예외를 처리하거나 처리해야합니다. 인터페이스에서 예외 처리? 그대로 두는 것이 좋습니다.
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
However, if you attempt to recover from an ExceptionInInitializerError
you are liable to run into a roadblock. The problem is that before throwing the error, the JVM marks the class that caused the problem as "failed". You simply won't be able to use it. Furthermore, any other classes that depend on the failed class will also go into failed state if they attempt to initialize. The only way forward is to unload all of the failed classes. That might be feasible for dynamically loaded code3, but in general it isn't.
1 - It is a compilation error if a static block unconditionally throws an unchecked exception.
2 - You might be able to intercept it by registering a default uncaught exception handler, but that won't allow you to recover, because your "main" thread can't start.
3 - If you wanted to recover the failed classes, you would need to get rid of the classloader that loaded them.
What was the reason behind this design decision?
It is to protect the programmer from writing code that throws exceptions that cannot be handled!
As we have seen, an exception in a static initializer turns a typical application into a brick. The best thing think that the language designers could do is to deal with the checked case as a compilation error. (Unfortunately, it is not practical to do this for unchecked exceptions as well.)
OK, so what should you do if your code "needs" to throw exceptions in a static initializer. Basically, there are two alternatives:
If (full!) recovery from the exception within the block is possible, then do that.
Otherwise, restructure your code so that the initialization doesn't happen in a static initialization block (or in the initializers of static variables).
Take a look at the Java Language Specifications: it is stated that it is a compile time error if static initializer
fails
is able to complete abruptly with a checked exception.
Since no code you write can call static initialization block, it is not useful to throw checked exceptions
. If it were possible, what would the jvm do when a checked exceptions are thrown? Runtimeexceptions
are propagated up.
For example: Spring's DispatcherServlet (org.springframework.web.servlet.DispatcherServlet) handles the scenario which catches a checked exception and throws another unchecked exception.
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());
}
I am able to compile throwing a checked Exception Also....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}
'programing tip' 카테고리의 다른 글
최대 절전 모드-일괄 업데이트가 업데이트에서 예기치 않은 행 개수를 반환했습니다. 0 실제 행 개수 : 0 예상 : 1 (0) | 2020.07.07 |
---|---|
IntelliJ 프로젝트 인덱스 재 구축 (0) | 2020.07.07 |
GitHub 위키를 어떻게 복제합니까? (0) | 2020.07.06 |
gcc에서 공유 라이브러리 함수의 정적 링크 (0) | 2020.07.06 |
Zip 폭탄은 어떻게 만드나요? (0) | 2020.07.06 |