2009-11-07 11 views
18

Ich stoße zufällig auf einen Java-Code an meinem Arbeitsplatz. Hier ist das Szenario: Es gibt 2 Klassen - ClassA und ClassB.öffentliche statische endgültige Variable in einer importierten Java-Klasse

ClassA hat nichts außer 4 öffentlichen statischen endgültigen String-Werte drin. Sein Zweck ist, diese Werte wie ClassA.variable zu verwenden (frag mich nicht warum, es ist nicht mein Code).

ClassB importiert ClassA. Ich habe die String-Werte in ClassA bearbeitet und kompiliert. Als ich lief ClassB konnte ich sehen, dass es die alten Werte verwendete - nicht die neuen Werte. Ich musste ClassB neu kompilieren, um neue Werte von ClassA zu verwenden! (Ich musste andere Klassen neu kompilieren, die ClassA importiert!)

Ist das nur wegen JDK 1.6 oder ich hätte früher ClassB auch neu kompilieren sollen! Kläre mich auf. :)

Antwort

23

Wenn die Werte der Variablen final von Klasse ClassA sein passieren Kompilierung-Konstanten, könnte die Compiler sie in die Klassen hat inlined ClassA anstatt mit einer Laufzeitreferenz zu erzeugen. Ich denke, das ist passiert in dem Fall, den Sie beschrieben haben.

Beispiel:

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

In diesem Beispiel wird der Compiler wahrscheinlich den Wert von in die FOO für Consumer erzeugten Code übernehmen anstelle der äquivalenten Laufzeitreferenz zu erzeugen. Wenn sich der Wert von FOO später ändert, müssen Sie Consumer neu kompilieren, damit er den neuen Wert verwendet.

Dies ist eine Optimierung, die einige Vorteile in Bezug auf Effizienz und Geschwindigkeit des Programms zusammengestellt hat. Zum Beispiel könnte den Wert inlining ermöglicht weitere Optimierungen in der Ausdrücken, die es verwendet, zum Beispiel:

int x = Flags.FOO * 10; 

In diesem Beispiel inlining den Wert (hier: 1) ermöglicht der Compiler zu bemerken, dass die Multiplikation Marken kein Unterschied und kann insgesamt weggelassen werden.

+1

also, Sie sagen, öffentliche statische final ist Kompilierzeitkonstante? Ich wusste das nicht. dachte, es war nur eine Konstante und kann nicht in der Laufzeit geändert werden! danke für Ihre Hilfe. –

+3

Gut anwser. Wenn Sie sehen möchten, dass die Variable inline ist, können Sie javap verwenden, um zu sehen, wie die Klasse kompiliert wurde, z. "javap -c Flags". –

3

Es ist ein Binärkompatibilitätsproblem. Verweise auf konstante Felder werden zur Kompilierzeit aufgelöst. Das Verhalten, das Sie sehen, ist korrekt. Wenn Sie die Werte in Klasse A ändern, müssen Sie den Client erneut kompilieren (Klasse B). Um solche Probleme zu vermeiden, sollten Sie Konstanten unter Verwendung eines Enum-Typs hinzufügen, der in Java Release 5.0 eingeführt wurde.

2

Warum versuchen Sie, die Klassen einzeln zu kompilieren?

Verwenden Sie ein Build-System wie Maven oder Ant oder lassen Sie Ihre IDE es tun.

Die einzige sichere Sache zu tun ist, jedes Java neu zu kompilieren, das von einer Java-Klasse abhängt, die geändert worden ist, bis jede Klasse, die bewirkt werden könnte, neu kompiliert worden ist.

+0

Die eine oder andere Klasse unterliegt möglicherweise nicht Ihrer Kontrolle. – DJClayworth

2

Wenn Sie nicht die Werte in einem Schalter verwenden, können Sie diese stattdessen tun:

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

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

dann wird der Compiler nicht mehr schwer, die Werte in den anderen Klassen codieren, die sie verwenden.

2

Angenommen KlasseA sieht wie folgt aus:

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

Wenn Sie es neu kompilieren, wird ClassB weiterhin die alten Werte verwenden. Ich denke, es könnte vom Compiler abhängen, aber ich denke, das ist das typische Verhalten. Wenn Sie ClassB jedes Mal eine Konstante in KlasseA Änderungen nicht neu kompilieren wollen, müssen Sie etwas zu tun haben:

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

Becuase jetzt javac nicht bereit ist, die Konstanten Inline. Stattdessen wird die CONST (int) -Methode aufgerufen, wenn der statische Initialisierer von ClassA ausgeführt wird.