89
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加载类时执行了一个块。但在这种情况下,类内的实例变量Mnofinal,因为该static块没有执行。

为什么呢?如果我删除final,它会正常工作吗?

首先分配哪个内存,static final变量还是static块?

如果由于final访问修饰符,类没有被加载,那么变量如何获得内存?

4

5 回答 5

138
  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按预期加载,并执行其静态初始化程序。

于 2013-05-31T09:15:28.503 回答
8

未加载该类的原因VALfinal AND它是用常量表达式(9090) 初始化的。当且仅当满足这两个条件时,才会在编译时评估常量并在需要时“硬编码”。

为了防止在编译时计算表达式(并使 JVM 加载您的类),您可以:

  • 删除 final 关键字:

    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; }
    
于 2013-06-04T23:53:30.203 回答
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 9090直接使用了静态最终值,因为 Mno.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);
    }
}
于 2013-06-05T19:05:25.530 回答
1
  1. 实际上,您还没有扩展该 Mno 类,因此当编译开始时,它将生成变量 VAL 的常量,并且当需要该变量时开始执行时,它从内存中加载。所以它不需要你的类引用以便不执行静态块。

  2. 如果 classA扩展 class Mno,则静态块包含在 class 中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);
        }
    }
    
于 2013-06-06T05:12:48.750 回答
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”类在另一段代码中实际使用和“执行”时初始化(=>打印)(我的情况是“new Statique()”。

于 2013-05-31T09:23:47.770 回答