2009-11-07 87 views
18

我碰巧在我的工作地點遇到了Java代碼。這裏是場景:有2個類 - ClassAClassB導入的java類中的公共靜態final變量

ClassA除了4個公共靜態的最終字符串值之外什麼也沒有。其目的是使用像ClassA.variable這些值(不要問我爲什麼,這不是我的代碼)。

ClassB進口ClassA。我編輯了ClassA中的字符串值並編譯了它。當我運行ClassB時,我可以看到它使用的是舊值 - 而不是新值。我必須重新編譯ClassB才能使用ClassA的新值! (我不得不重新編譯其他類,導入ClassA!)

這是因爲JDK 1.6還是我應該早些知道重新編譯ClassB也!開導我。 :)

回答

23

如果來自類ClassAfinal變量的值恰好是編譯時常量,編譯器可能會使用ClassA將它們內聯到類中,而不是生成運行時引用。我想,這就是你所描述的情況。

實施例:

public class Flags { 
    public static final int FOO = 1; 
    public static final int BAR = 2; 
} 

public class Consumer { 
    public static void main(String[] args) { 
     System.out.println(Flags.FOO); 
    } 
} 

在該示例中,編譯器將可能合併的FOO值到用於Consumer產生,而不是產生等效的運行時參考的代碼。如果FOO的值稍後發生更改,則必須重新編譯Consumer以使其使用新值。

這是一個優化,它在編譯程序的效率和速度方面具有一些優點。例如,內聯值可能啓用的表達式,用它進一步的優化,例如:

int x = Flags.FOO * 10; 

在這個例子中,內聯值(這裏是:1)使編譯器注意到,該乘法品牌沒有區別,可以一起省略。

+1

所以,你說公共靜態最終是編譯時間常量?不知道。認爲這只是一個常量,不能在運行時修改!謝謝你的幫助。 – 2009-11-07 14:28:40

+3

好的anwser。如果你想看到變量被內聯,你可以使用javap來查看類是如何編譯的,例如「javap -c標誌」。 – 2009-11-07 14:31:55

3

這是一個二進制兼容性問題。在編譯時解析常量字段的引用。你所看到的行爲是正確的;如果您更改A類中的值,那麼您將不得不重新編譯客戶端(B類)。爲了避免這樣的問題,可以考慮使用Java 5.0中引入的枚舉類型來添加常量。

2

你爲什麼要單獨編譯類?

使用像maven或ant這樣的構建系統,或者讓你的IDE做到這一點。

要做的唯一安全的事情就是重新編譯每個依賴於java類的java,這個java類已經改變了,直到每個可能受影響的類都被重新編譯。

+0

其中一個或其他類可能不受您的控制。 – DJClayworth 2010-07-02 17:45:03

2

如果您沒有在交換機使用的值,你可以這樣做,而不是:

public class A 
{ 
    public static final int FOO; 
    public static final String BAR; 

    static 
    { 
     FOO = 42; 
     BAR = "Hello, World!"; 
    } 
} 

那麼編譯器將不再難在使用它們的其他類代碼值。

2

假設ClassA的是這樣的:

public class ClassA { 
    public static final int FOO = 1; 
    public static final int BAR = 2; 
} 

如果重新編譯它,ClassB的會繼續使用舊的價值觀念。我想這可能取決於編譯器,但我認爲這是典型的行爲。如果你不想重新編譯ClassB的在ClassA的變化,每次一個常數,你必須做這樣的事情:

public class ClassA { 
    public static final int FOO = CONST(1); 
    public static final int BAR = CONST(2); 

    public static int CONST(int i) { return i; } 
} 

監守現在的javac不願內嵌的常量。相反,它會在ClassA的靜態初始化器運行時調用CONST(int)方法。