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
модификатора доступа класс не загружается, то как переменная может получить память?
java
static
access-modifiers
Стхита
источник
источник
Ответы:
static final int
Поле является константой времени компиляции и его значение зашито в класс назначения без ссылки на его происхождение;В частности, скомпилированный байт-код соответствует этому:
public static void main(String arg[]){ System.out.println("**MAIN METHOD"); System.out.println(9090) System.out.println(9190) }
Как только вы удалите
final
, это больше не будет постоянной времени компиляции, и особое поведение, описанное выше, не применяется.Mno
Класс загружается как вы ожидаете , и его статический инициализатор выполняет.источник
Причина , почему класс не загружен в том , что
VAL
этоfinal
и он инициализирован с выражением постоянной (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; }
источник
Если вы видите использование сгенерированного байт-кода
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); } }
источник
На самом деле вы не расширяли этот класс Mno, поэтому при запуске компиляции он генерирует константу переменной VAL, а при запуске выполнения, когда эта переменная требуется, ее загрузка происходит из памяти. Таким образом, не требуется, чтобы ваша ссылка на класс, чтобы статический блок не выполнялся.
если класс
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); } }
источник
Насколько мне известно, он будет выполняться в порядке появления. Например :
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» фактически используется и «выполняется» в другом фрагменте кода (в моем случае я использовал «new Statique ()».
источник
Statique
класс, выполняяnew Statique()
. Хотя в заданном вопросеMno
класс вообще не загружается.