2012-12-18 3 views
2

다음 코드가 무한 재귀 일종의 힙 (OutOfMemoryError 또는 무언가)에 해당하는 스택 오버플로를 일으키는 이유가 무엇인지 이해할 수 없습니다. 정적 초기화가 이런 식으로 경비를 제공하고 대신 NullPointerException을 throw합니까?오브젝트/정적 작성에서 무한 재귀/왜이 코드가 NullPointerException을 던지고 있습니까?

편집 : 이해가 안된다고 생각합니다. public static void main(String[] args)StaticClass에서 정적 메서드를 호출합니다. 이 메서드를 사용하기 전에 먼저 StaticClass을로드해야합니다. doSmth()이라고합니다. 클래스가로드되면 모든 정적 코드가 실행됩니다. StaticClass 클래스에서 objClassnew ObjClass()의 값을 가진 정적 필드이므로 null (throw NullPointerException)이되어서는 안됩니다. 분명히 new ObjClass()은 인스턴스화 할 수 없습니다.이 작업을 수행하려면 StaticClass을로드해야하며 StaticClass이로드 될 때 이미 발생합니다 (따라서 무한 재귀 또는 유추 교착 상태 비유). 문제는 JVM이 objClass이 어떤 종류의 반복 호출 (recurrent call)을하기 때문에 new ObjClass()을 초기화 할 수 없다는 대신 null이라는 것입니다.

정상적인 경우 호출자 때문에 NullPointerException이 발생합니다. 그러나이 경우 호출 수신자 만 변경할 수 있습니다 (끝에있는 주석이있는 행을 ObjClass 생성자에서 제거). NullPointerException을 수신하지 않습니다. 클래스 로딩 JVM은 ObjClass에 대한 인스턴스를 생성 (인스턴스 생성이 아직 완료되지 않음)라는 시간 ObjClass 생성자 nullobjClass 점을 참조하려고

Exception in thread "main" java.lang.ExceptionInInitializerError 
    at pack.ObjClass.main(ObjClass.java:28) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 
Caused by: java.lang.NullPointerException 
    at pack.ObjClass$StaticClass.doSmth(ObjClass.java:39) 
    at pack.ObjClass.<init>(ObjClass.java:20) 
    at pack.ObjClass$StaticClass.<clinit>(ObjClass.java:34) 
    ... 6 more 

답변

1
private static ObjClass objClass = new ObjClass(); 

동안 : 제공

package pack; 

public class ObjClass 
{ 
    public ObjClass() { 
     StaticClass.doSmth();//if removed, no NullPointerException 
    } 

    public String getSomething() { 
     return "get"; 
    } 

    public static void main(String[] args) { 
     StaticClass.loadStaticClass(); 
    } 
} 

class StaticClass { 
    private static ObjClass objClass = new ObjClass(); 

    static void loadStaticClass() { 
    } 

    static void doSmth()    { 
     System.out.println(objClass.getSomething()); 
    } 
} 

. \

objClass.getSomething()을 호출하려고하면 0을 호출하는 중입니다.을 null 참조로 설정하면 NullPointerException이됩니다.

+0

그게 내가 이해하지 못하는 이유입니다. 'StaticClass'의 로딩이 끝나기 전에 어떻게'doSmth()'메소드를 평가할 수 있습니까? 그리고 클래스가 완료 될 때까지 객체를로드하는 'objClass'는 null이 아닌 객체를 가리켜 야합니다; ObjClass를 인스턴스화 할 수 없다면 예외를 던져야합니다. – m3th0dman

+0

사실 정적 초기화 프로그램에 문제가 있었고 그 원인은'NullPointerException'입니다. – m3th0dman

4

이 문제에 대한 복잡한 대답이 있어야 어떻게 결정 되든간에 매우 간단합니다. null 값을 역 참조했기 때문에 NullPointerException이 발생합니다. 아무것도 더. 이 초기화 중 완료하고 그 전에 참조를 검사 할 수 초기화되고있어도하는 finalnull 될 것입니다 전에, 하나 개의 클래스가 서로를 호출 할 수 있습니다 볼 수 있듯이

public class Main { 
    public static void main(String... args) { 
     new A(); 
    } 

    static class A { 
     static { 
      System.out.println("Start of class A initialisation, A.b is " + A.b); 
     } 

     static final B b = new B(); 

     static void method(String from) { 
      System.out.println("Called a method in A from " + from + ", A.b is " + A.b); 
      B.method(); 
     } 

     static { 
      System.out.println("End of class A initialisation, A.b is " + A.b); 
     } 
    } 

    static class B { 
     static { 
      System.out.println("Start of class B initialisation, A.b is " + A.b); 

      A.method("B static block"); 

      System.out.println("End of class B initialisation, A.b is " + A.b); 
     } 

     B() { 
      A.method("B() constructor"); 
      System.out.println("Only after this should A.b be set."); 
     } 

     static void method() { 
      System.out.println("Called a method in B, A.b is " + A.b); 
     } 
    } 
} 

인쇄

Start of class A initialisation, A.b is null 
Start of class B initialisation, A.b is null 
Called a method in A from B static block, A.b is null 
Called a method in B, A.b is null 
End of class B initialisation, A.b is null 
Called a method in A from B() constructor, A.b is null 
Called a method in B, A.b is null 
Only after this should A.b be set. 
End of class A initialisation, A.b is [email protected] 

하나. objClass가 아직 설정되지 않았기 때문에


그것은 NullPointerException이 던지고.

생성자에서 반환 할 때까지 설정되지 않지만 생성자에 있으므로 기본값은 null입니다.

BTW, 이것은 다음과 같은 코드가이되지 않는 이유 이해가 안 http://en.wiktionary.org/wiki/infinite_recursion

처럼 아무것도없는 힙 (OutOfMemoryError를 또는 무언가)에 대한 스택 오버플로 등가 그들이

의 원인 모든 다른 오류는 특별한 의미를 지닙니다. 참조가 초기화되지 않고 사용될 때 발생하는 오류는 NullPointerException입니다.

정적 초기화는 이와 같은 상황을 방지하고 대신 NullPointerException을 throw합니까?

필드가 정적 일 필요가 없습니다. 정적 필드가 초기화되기 전에 액세스하면 비 정적 필드에도 마찬가지입니다.

StaticClass가로드 될 때 objClass가 인스턴스화되어야합니까?

실제로 완료되기 전에 예외를 throw하므로 실제로로드되지 않습니다. 이 후에이 클래스를 사용하려고하면 클래스를로드하지 못했기 때문에 NoClassDefError가 발생합니다.

+0

왜 NullPointerException이 던져 졌는지 이유를 알고 있습니다. 문제는'StaticClass'가로드 될 때 (실제로 doSmth()를 호출하기 전에)'objClass'가 인스턴스화되어야하고 NullPointerException은 거기에 있으면 안된다는 것입니다. 그러나'StaticClass'가로드 될 때'objClass'는'StaticClass'가로드되고 어떤 종류의 무한 재귀 (또는 교착 상태)가 필요하기 때문에 인스턴스화 될 수 없습니다. – m3th0dman

+0

또는 논리 버그. 처음에 끝내야 할 코드가있을 때마다 작동 할 수없는 코드를 만들었습니다. –

+0

예, 사실은 분명히 논리 버그입니다. 하지만 왜 NullPointerException? – m3th0dman

4

StaticClass을 참조하면 이 생성되며 ObjClass 생성자를 호출하며 이는 doSmth()을 호출합니다. 여기에는 objClass이라는 참조 번호가 사용되지만 에는 아직 할당되지 않은이 있습니다. 참조 용으로 완전하게 구성된 objClass이 필요합니다.

간단히

this.x = doMethod(); 

레퍼런스 x 결과가 할당되기 전에 완료 doMethod() 필요하다.

무엇을하려하십니까? 아니면 어떤 종류의 운동입니까?

+0

자바 평가 전략을 잘 알고 있습니다. 그게 문제가 아니야. 문제는 'objClass'가 할당되지 않은 이유입니다. 왜 그것은 무한 재귀를 입력하지 않으며 JVM은 첫 번째 시도 후에 포기하지 않습니다. 'StaticClass'가로드 될 때'static ObjClass objClass = new ObjClass()'가 있기 때문에'objClass'가 인스턴스화되어야합니까? – m3th0dman

+0

그리고 어떻게 그 참조를 인스턴스화합니까? ObjClass 생성자를 호출합니다. 참조가 완료 될 때까지 할당되지 않았습니다. 그렇지 않으면 다른 스레드가 해당 참조를 사용할 수 있고 불완전한 객체를 가리킬 수 있습니다. –

+0

왜 doSmth()의 내용으로 이동하는 것이 가능합니까? 메소드를 호출하여 StaticClass 클래스를로드하십시오. 'ObjClass' 생성자를 호출하지 않으면 왜 예외가 던지지 않습니까? – m3th0dman

관련 문제