Java가 정적 초기화 블록에서 확인 된 예외를 발생시키지 않는 이유는 무엇입니까? 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위에서 ExceptionInInitializerError2 를 붙잡을 수있는 곳은 없습니다 .

어떤 경우에는 그것을 잡을 수 있습니다. 예를 들어,을 호출하여 클래스 초기화를 트리거 한 경우 Class.forName(...)호출을에 묶고 a 또는 그 이후를 try잡을 수 있습니다 .ExceptionInInitializerErrorNoClassDefFoundError

당신이 시도 할 경우, 복구 에서 ExceptionInInitializerError당신은 장애물로 실행되기 쉽다. 문제는 오류를 발생시키기 전에 JVM이 문제를 일으킨 클래스를 “실패”로 표시한다는 것입니다. 당신은 단순히 그것을 사용할 수 없습니다. 또한 실패한 클래스에 의존하는 다른 클래스도 초기화하려고하면 실패한 상태가됩니다. 앞으로 나아가는 유일한 방법은 모든 실패한 클래스를 언로드하는 것입니다. 즉 동적으로로드 된 코드에 대한 실현 가능한 3 하지만, 일반적으로는 없습니다.

1-정적 블록 이 검사되지 않은 예외를 무조건 발생시키는 경우 컴파일 오류 입니다.

2 – 당신은 수있는 기본 캐치되지 않는 예외 핸들러를 등록하여 차단을 할 수있을, 그러나 그것은 당신의 “주”스레드를 시작할 수 없습니다 때문에, 복구 할 수 없습니다.

3-실패한 클래스를 복구하려면 해당 클래스를로드 한 클래스 로더를 제거해야합니다.


이 디자인 결정의 이유는 무엇입니까?

처리 할 수없는 예외를 발생시키는 코드를 작성 하지 않도록 프로그래머를 보호합니다 !

앞에서 본 것처럼 정적 초기화 프로그램의 예외는 일반적인 응용 프로그램을 브릭으로 바꿉니다. 언어 디자이너가 할 수있는 최선의 방법은 체크 된 사례를 컴파일 오류로 처리하는 것입니다. (불행히도, 확인되지 않은 예외에 대해서도이를 수행하는 것은 실용적이지 않습니다.)


코드가 정적 초기화 프로그램에서 예외를 발생시키기 위해 “필요한”경우 어떻게해야합니까? 기본적으로 두 가지 대안이 있습니다.

  1. 블록 내 예외에서 (full!) 복구가 가능하면 그렇게하십시오.

  2. 그렇지 않으면 정적 초기화 블록 (또는 정적 변수의 초기화 자)에서 초기화가 발생하지 않도록 코드를 재구성하십시오.


답변

Java 언어 사양을 살펴보십시오. 정적 초기화 프로그램 이 실패하면 확인 된 예외로 갑자기 완료 될 수있는 경우 컴파일 시간 오류라고 명시되어 있습니다 .


답변

작성한 코드는 정적 초기화 블록을 호출 할 수 없으므로 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());
    }