2015-04-07 16 views
7

Tôi có lớp AbstractsAndInterfaces:hành vi kỳ lạ của các biến tĩnh

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

Tại sao nó in 5?

Tại sao BASE biến là chưa được khởi tạo?

+2

Về cơ bản: http://stackoverflow.com/questions/27859435/java-static-final-field-initialization-order – marvin82

Trả lời

10

Đó là bởi vì:

private static final int DELTA = 5; sẽ là một thời gian biên dịch liên tục. Vì vậy, 5 đã sẵn sàng (khởi tạo) khi lớp được khởi tạo (khi nó được nạp).

Dòng đầu tiên đó được thực hiện là: public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces();

Vì vậy, bây giờ bạn sẽ được chuyển tới:

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 
} 

Trong đoạn mã trên, DELTA sẽ là 5 nhưng BASE sẽ là 0 vì nó là không phải final. Thay đổi BASE thành final sẽ thay đổi kết quả thành 12.

CHỈNH SỬA:

Mã mẫu để hiển thị trường hợp OP với mã byte.

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) { 

    } 

} 

Trong đoạn mã trên, khi chương trình được chạy , sản lượng sẽ 5 và không 10. Bây giờ, hãy xem mã byte .

Mã byte của constructor trông như thế này:

p

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 

đang Byte cho khối init tĩnh:

tĩnh {}; mô tả:() V cờ: ACC_STATIC Code: chồng = 2, người dân địa phương = 0, args_size = 0 0: mới # 1 // lớp mẫu 3: dup 4: invokespecial # 15 // Phương thức "" :() V 7: putstatiC# 18 // Trường s: LSample; 10: bipush 10 12: putstatiC# 20 // Dòng y: Tôi 15: return LineNumberTable: dòng 3: 0 dòng 5: 10 dòng 1: 15 LocalVariableTable: Bắt đầu Chiều dài khe Tên Chữ ký

Nếu bạn kiểm tra cẩn thận, x không được khởi tạo trong init tĩnh của lớp. Chỉ y đang được đặt là tĩnh. Nhưng khi bạn nhìn vào hàm tạo, bạn sẽ thấy rằng hằng số 5 được sử dụng trực tiếp (iconst_5) và được đẩy lên ngăn xếp.

Bây giờ nếu bạn thêm đoạn mã sau vào main():

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

Mã byte cho main() sẽ là:

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

Một lần nữa quan sát rằng x đang được sử dụng như iconst_5 trong khi y đang được tìm nạp/gọi gián tiếp.

+0

dòng cuối cùng của bạn là sai thay đổi nó !! – Prashant

+2

@Prashant - Ya .. Typo, bị nhầm lẫn với tên biến .. Thay đổi ngay bây giờ :) – TheLostMind

+2

Không phải tất cả các trường đều được khởi tạo trước khi hàm tạo được gọi? Đó là điều mà [JLS] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4) nói rõ .. Tôi nghĩ nó đáng nhắc đến tại sao 'BASE' là vẫn chưa được khởi tạo. – Maroun