Статический блок в Java не выполнен

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 класс не загружается, то как переменная может получить память?

Ответ 1

  • Поле A static final int является константой времени компиляции, и ее значение жестко закодировано в целевом классе без ссылки на его начало;
  • поэтому ваш основной класс не вызывает загрузку класса, содержащего это поле;
  • поэтому статический инициализатор в этом классе не выполняется.

В частности, скомпилированный байт-код соответствует этому:

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

Как только вы удаляете final, он больше не является константой времени компиляции, и описанное выше особое поведение не применяется. Класс Mno загружается так, как вы ожидаете, и его статический инициализатор выполняет.

Ответ 2

Причина, по которой класс не загружен, заключается в том, что VAL is final И он инициализируется с помощью константного выражения (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; }
    

Ответ 3

Если вы видите сгенерированный байт-код с помощью 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);
    }
}

Ответ 4

1) На самом деле вы не расширили этот класс Mno, поэтому при запуске компиляции он будет генерировать константу переменной VAL и когда запуск начнется, когда эта переменная понадобится, ее загрузка будет осуществляться из памяти. Так что не требуется ваша ссылка на класс, чтобы статический bock не выполняется.

2), если класс A расширяет этот класс Mno в то время, когда статический блок включен в класс A, если вы это сделаете, тогда выполняется статический блок. например..  открытый класс A расширяет 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);
    }
}

Ответ 5

Насколько я знаю, он будет выполнен в порядке появления. Например:

 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

Я только что протестировал его, и статика инициализируется (= > print), когда класс "Статика" фактически используется и "выполняется" в другом фрагменте кода (в моем случае я сделал "новую Statique()".