2015-04-07 4 views
7

У меня есть класс AbstractsAndInterfaces:Странное поведение статических переменных

public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces(); 
private static final int DELTA = 5; 

private static int BASE = 7; 

private int x; 
public AbstractsAndInterfaces() 
{ 
    //System.out.println(BASE); 
    //System.out.println(DELTA); 
    x = BASE + DELTA; 
} 
public static int getBASE() 
{ 
    return BASE; 
} 

    /** 
* @param args the command line arguments 
*/ 
public static void main(String[] args) { 

    System.out.println(AbstractsAndInterfaces.instance.x); 
} 

Почему печать 5?

Почему BASE Переменная неинициализирована?

+2

По существу: http://stackoverflow.com/questions/27859435/java-static-final-field-initialization-order – marvin82

ответ

10

Это потому, что:

private static final int DELTA = 5; будет время компиляции постоянной. Таким образом, 5 уже будут доступны (инициализированы), когда класс будет инициализирован (после его загрузки).

Первая линия, которая запускается на выполнение является: public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces();

Итак, теперь вы будете ехать на:

public AbstractsAndInterfaces() 
{ 
    //System.out.println(BASE); 
    //System.out.println(DELTA); 
    x = BASE + DELTA; // same as x= 0+5 i.e default value of fields + constant value 5 
} 

В приведенном выше коде, DELTA будет 5, но BASE будет 0, как это не final. Изменение BASE в окончательном варианте изменит результат на 12.

EDIT:

Пример кода, чтобы показать сценарий Ор с байт-код.

public class Sample { 
    static Sample s = new Sample(); 
    static final int x = 5; 
    static int y = 10; 

    public Sample() { 
     int z = x + y; 
     System.out.println(z); 
    } 

    public static void main(String[] args) { 

    } 

} 

В приведенном выше коде, когда программа запустить, выход будет 5 и не 10. Теперь давайте посмотрим на байт-код .

байт-код конструктора выглядит следующим образом:

р

ublic Sample(); 
    descriptor:()V 
    flags: ACC_PUBLIC 
    Code: 
    stack=2, locals=2, args_size=1 
     0: aload_0 
     1: invokespecial #24     // Method java/lang/Object."<init> 
:()V 
     4: iconst_5  ------->   // Here 5 is used directly as it is a compile time constant 
     5: getstatic  #20 ------->  // Field y:I 
     8: iadd 
     9: istore_1 
     10: getstatic  #25     // Field java/lang/System.out:Ljav 
/io/PrintStream; 
     13: iload_1 
     14: invokevirtual #31     // Method java/io/PrintStream.prin 
ln:(I)V 
     17: return 

байт-код для статического блока инициализации:

статическая {}; дескриптора:() V флаги: ACC_STATIC Код: стек = 2, местные жители = 0, args_size = 0 0: новый # 1 // Класс Sample 3: DUP 4: invokespecial # 15 // Метод "" :() V 7: putstatiC# 18 // Поле s: LSample; 10: bipush 10 12: putstatiC# 20 // Поле у: I 15: возвращение LineNumberTable: линия 3: 0 линия 5: 10 линия 1: 15 LocalVariableTable: Начало Длина Slot Имя Подпись

Если вы тщательно проверите, x не инициализируется в статическом инициализации класса. Только y устанавливается как статичный. Но когда вы посмотрите на конструктор, вы увидите, что константа 5 используется непосредственно (iconst_5) и вставляется в стек.

Теперь, если добавить следующий код в main():

public static void main(String[] args) { 
    System.out.println(Sample.x); 
    System.out.println(Sample.y); 
} 

байт-код для main() будет:

public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: getstatic  #25     // Field java/lang/System.out:Ljav 
a/io/PrintStream; 
     3: iconst_5 -->      // No "x" but a constant value instead of "x" 
     4: invokevirtual #31     // Method java/io/PrintStream.prin 
tln:(I)V 
     7: getstatic  #25     // Field java/lang/System.out:Ljav 
a/io/PrintStream; 
     10: getstatic  #20     // Field y:I --> but "y" is being fetched 
     13: invokevirtual #31     // Method java/io/PrintStream.prin 
tln:(I)V 
     16: return 
     LineNumberTable: 
     line 13: 0 
     line 14: 7 
     line 15: 16 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      0  17  0 args [Ljava/lang/String; 
} 

Снова заметим, что x используется как iconst_5 тогда y это время Принято/указано косвенно.

+0

Ваша последняя строка неверна. – Prashant

+2

@Prashant - Ya .. Typo, запутался с именами переменных .. Изменено это сейчас :) – TheLostMind

+2

Не должны ли все поля инициализироваться до вызова конструктора? Вот что говорит [JLS] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4). Я думаю, стоит упомянуть, почему «BASE» все еще неинициализированы. – Maroun

Смежные вопросы