Java의 정적 블록이 실행되지 않음


87
class Test {
    public static void main(String arg[]) {    
        System.out.println("**MAIN METHOD");
        System.out.println(Mno.VAL); // SOP(9090);
        System.out.println(Mno.VAL + 100); // SOP(9190);
    }

}

class Mno {
    final static int VAL = 9090;
    static {
        System.out.println("**STATIC BLOCK OF Mno\t: " + VAL);
    }
}

static클래스가로드 될 때 블록이 실행 된다는 것을 알고 있습니다. 그러나이 경우에는 인스턴스 변수의 내부 클래스 Mno입니다 final때문에 그의,static 블록은 실행되지 않는다.

왜 이렇게이다? 을 제거하면 final제대로 작동합니까?

어떤 메모리가 먼저 할당 됩니까 , static final변수 또는 static블록?

받는 사람에 의한 경우 final액세스 수정 클래스는로드되지 않습니다, 그럼 어떻게 변수 GET 메모리 할 수 있습니까?


1
정확한 오류와 메시지는 무엇입니까?
Patashu

@Patashu, 오류가 없습니다, 그 의심
Sthita

답변:


134
  1. static final int필드는 인 컴파일 시간 상수 와 그 값이 원점에 대한 참조없이 대상 클래스로 하드 코딩되고;
  2. 따라서 메인 클래스는 필드를 포함하는 클래스의 로딩을 트리거하지 않습니다.
  3. 따라서 해당 클래스의 정적 초기화 프로그램은 실행되지 않습니다.

구체적으로 컴파일 된 바이트 코드는 다음과 같습니다.

public static void main(String arg[]){    
    System.out.println("**MAIN METHOD");
    System.out.println(9090)
    System.out.println(9190)
}

제거하자마자 final더 이상 컴파일 타임 상수가 아니며 위에서 설명한 특수 동작이 적용되지 않습니다. Mno클래스는 예상대로로드 및 실행하는 초기화 정적된다.


1
그렇다면 클래스를로드하지 않고 클래스의 최종 변수 값을 어떻게 평가할까요?
Sumit Desai 2013 년

18
모든 평가는 컴파일 시간에 발생하며 최종 결과는 변수를 참조하는 모든 위치에 하드 코딩됩니다.
Marko Topolnik 2013 년

1
따라서 기본 변수 대신 일부 Object이면 이러한 하드 코딩이 불가능합니다. 그렇지 않나요? 그렇다면이 경우 해당 클래스가로드되고 정적 블록이 실행 될까요?
Sumit Desai 2013 년

2
Marko, Sumit의 의심은 기본이 아닌 Object라면 그러한 하드 코딩은 불가능할 것입니다. 그렇지 않나요? 그렇다면이 경우 해당 클래스가로드되고 정적 블록이 실행 될까요?
Sthita 2013 년

8
@SumitDesai 정확히, 이것은 원시 값과 문자열 리터럴에 대해서만 작동합니다. 전체 세부 사항에 대한 읽기 Java 언어 사양의 관련 장
마르코 Topolnik

8

클래스가로드되지 않은 이유 VALfinal AND상수 표현식 (9090)으로 초기화되기 때문입니다 . 이 두 조건이 충족되는 경우에만 상수가 컴파일 시간에 평가되고 필요한 경우 "하드 코딩"됩니다.

컴파일 타임에 표현식이 평가되는 것을 방지하고 JVM이 클래스를로드하도록하려면 다음 중 하나를 수행 할 수 있습니다.

  • 마지막 키워드를 제거하십시오.

    static int VAL = 9090; //not a constant variable any more
    
  • 또는 오른쪽 표현식을 상수가 아닌 것으로 변경합니다 (변수가 여전히 최종인 경우에도).

    final static int VAL = getInt(); //not a constant expression any more
    static int getInt() { return 9090; }
    

5

를 사용하여 생성 된 바이트 코드를 javap -v Test.class보면 main ()이 다음과 같이 나옵니다.

public static void main(java.lang.String[]) throws java.lang.Exception;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String **MAIN METHOD
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: sipush        9090
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: sipush        9190
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: return        

11: sipush 9090Mno.VAL은 컴파일 시간 상수이기 때문에 정적 최종 값이 직접 사용된다는 것을 " " 에서 명확하게 볼 수 있습니다 . 따라서 Mno 클래스를로드 할 필요가 없습니다. 따라서 Mno의 정적 블록은 실행되지 않습니다.

아래와 같이 Mno를 수동으로로드하여 정적 블록을 실행할 수 있습니다.

class Test{
    public static void main(String arg[]) throws Exception {
        System.out.println("**MAIN METHOD");
        Class.forName("Mno");                 // Load Mno
        System.out.println(Mno.VAL);
        System.out.println(Mno.VAL+100);
    }

}

class Mno{
    final static int VAL=9090;
    static{
        System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
    }
}

1
  1. 실제로 Mno 클래스를 확장하지 않았으므로 컴파일이 시작될 때 변수 VAL의 상수를 생성하고 해당 변수가 필요할 때 실행이 시작될 때 메모리에서로드됩니다. 따라서 정적 bock이 실행되지 않도록 클래스 참조가 필요하지 않습니다.

  2. class가 class를 A확장 Mno하면 정적 블록이 클래스에 포함됩니다. A이렇게하면 해당 정적 블록이 실행됩니다. 예를 들어 ..

    public class A extends Mno {
    
        public static void main(String arg[]){    
            System.out.println("**MAIN METHOD");
            System.out.println(Mno.VAL);//SOP(9090);
            System.out.println(Mno.VAL+100);//SOP(9190);
        }
    
    }
    
    class Mno {
        final static int VAL=9090;
        static {
            System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
        }
    }
    

0

내가 아는 한, 그것은 출현 순서대로 실행될 것입니다. 예 :

 public class Statique {
     public static final String value1 = init1();

     static {
         System.out.println("trace middle");
     }
     public static final String value2 = init2();


     public static String init1() {
         System.out.println("trace init1");
         return "1";
     }
     public static String init2() {
         System.out.println("trace init2");
         return "2";
     }
 }

인쇄됩니다

  trace init1
  trace middle
  trace init2

방금 테스트하고 "Statique"클래스가 실제로 사용되고 다른 코드에서 "실행"될 때 정적이 초기화 (=> print)됩니다 (제 경우에는 "new Statique ()"를 수행했습니다.).


2
Statique을 수행 하여 클래스를 로드하기 때문에이 출력이 표시 됩니다 new Statique(). 질문에서 Mno클래스가 전혀로드되지 않았습니다.
RAS

@Fabyen, 다음과 같이 테스트 클래스에서 Mno의 개체를 만드는 경우 : Mno anc = New Mno (); 그런 다음 괜찮지 만 현재 시나리오는 그렇게하지 않습니다. 내 의심은 최종 제거하는 경우 정적 블록이 잘 실행되고 그렇지 않으면 실행되지 않습니다.
Sthita 2013 년

1
아래 대답은 완벽합니다. Main.class의 바이트 코드 (Mno.VAL 사용)에서 9090은 하드 코딩 된 것으로 확인됩니다. final을 제거하고 컴파일 한 다음 javap Main을 사용하면 getstatic # 16 이 표시됩니다 . // 필드 Statique.VAL : I . 최종적으로 다시 넣고 컴파일 한 다음 javap Main을 사용하면 sipush 9090 이 표시 됩니다 .
Fabyen 2013 년

1
Main.class에 하드 코딩 되었기 때문에 MNO 클래스를로드 할 이유가 없으므로 정적 초기화가 없습니다.
Fabyen 2013 년

이것은 두 번째 질문에 대한 답입니다. "어떤 메모리가 먼저 할당됩니까, 정적 최종 변수입니까, 아니면 정적 블록입니까?" (어휘 순서)
Hauke ​​Ingmar Schmidt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.