Статический блок в 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()".