Java中的静态块未执行

Java中的静态块未执行,java,static,access-modifiers,Java,Static,Access Modifiers,我知道加载类时会执行静态块。但是在这种情况下,类Mno中的实例变量是final,因为静态块没有执行 为什么会这样?如果我删除最终版,它会正常工作吗 将首先分配哪个内存,static final变量或static块 如果由于finalaccess修饰符,类没有被加载,那么变量如何获得内存 静态final int字段是一个编译时常量,它的值被硬编码到目标类中,而不引用它的原点 因此,主类不会触发包含该字段的类的加载 因此,不会执行该类中的静态初始值设定项 具体而言,编译的字节码对应于: class

我知道加载类时会执行
静态
块。但是在这种情况下,类
Mno
中的实例变量是
final
,因为
静态
块没有执行

为什么会这样?如果我删除
最终版
,它会正常工作吗

将首先分配哪个内存,
static final
变量或
static

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

  • 静态final int
    字段是一个编译时常量,它的值被硬编码到目标类中,而不引用它的原点
  • 因此,主类不会触发包含该字段的类的加载
  • 因此,不会执行该类中的静态初始值设定项 具体而言,编译的字节码对应于:

    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);
        }
    }
    

    一旦删除
    final
    ,它就不再是编译时常量,并且上面描述的特殊行为不适用。
    Mno
    类按预期加载,并执行其静态初始值设定项。

    据我所知,它将按外观顺序执行。例如:

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

     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";
         }
     }
    

    我刚刚测试了它,当类“Statique”实际使用并在另一段代码中“执行”时,静态被初始化(=>print)(我的例子是“new Statique()”)

    未加载该类的原因是
    VAL
    final
    并且它是用(9090)初始化的。如果且仅当满足这两个条件时,将在编译时计算该常数,并在需要时进行“硬编码”

    要防止在编译时对表达式求值(并使JVM加载您的类),您可以:

    • 删除最后一个关键字:

        trace init1
        trace middle
        trace init2
      
    • 或者将右侧表达式更改为非常量(即使变量仍然是final):


    如果您看到使用
    javap-v Test.class生成的字节码,main()的结果如下所示:

    final static int VAL = getInt(); //not a constant expression any more
    static int getInt() { return 9090; }
    
    您可以在“
    11:sipush 9090
    ”中清楚地看到,直接使用了静态终值,因为Mno.VAL是编译时常量。因此不需要加载Mno类。因此不会执行Mno的静态块

    您可以通过手动加载Mno来执行静态块,如下所示:

    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        
    
  • 实际上,您还没有扩展该Mno类,所以当编译开始时,它将生成变量VAL的常量,当执行开始时,当需要该变量时,它将从内存加载。因此,不需要您的类引用,以便不执行静态块

  • 如果类
    A
    扩展类
    Mno
    ,则静态块包含在类
    A
    中。如果执行此操作,则执行该静态块。 例如

    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);
        }
    }
    

  • 您得到的确切错误和消息是什么?@Patashu,没有错误,这是一个疑问,但是,在不加载类的情况下,如何计算类中最后一个变量的值呢?所有计算都在编译时进行,最终结果都硬编码到引用该变量的所有位置。因此,如果不是原始变量,而是如果是某个对象,那么这种硬编码是不可能的。不是吗?那么,在这种情况下,会加载该类并执行静态块吗?Marko,Sumit的怀疑是正确的,如果不是原语,而是某个对象,那么这种硬编码是不可能的。是吗?那么,在这种情况下,会加载该类并执行静态块吗已执行?@SumitDesai完全正确,这仅适用于基本值和字符串文字。有关详细信息,请阅读您获得此输出的原因,因为您正在通过执行
    new Statique()加载
    Statique
    。在提问时,
    Mno
    类根本没有加载。@fabyn,如果我在测试类中创建Mno对象,如下所示:Mno anc=New Mno();那么很好,但是当前的场景我没有这样做,我的疑问是如果我删除final,那么静态块会很好地执行,否则它不会执行,为什么会这样?是的,下面的答案是完美的。在Main.class的字节码中(使用Mno.VAL),9090是硬编码的。删除final,编译,然后使用javap Main,您将看到getstatic#16;//Field Statique.VAL:I。放回final,编译,然后使用javap Main,您将看到sipush 9090。因为它是在Main.class中硬编码的,所以没有理由加载类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);
        }
    }
    
    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);
        }
    }