2009-11-07 22 views
18

Zdarza się, że natknąłem się na kod Java w moim miejscu pracy. Oto scenariusz: istnieją 2 klasy - ClassA i ClassB.publiczna statyczna zmienna końcowa w zaimportowanej klasie java

ClassA ma nic oprócz 4 publicznych statycznych wartości końcowych ciągów wewnątrz niego. Jego celem jest użycie wartości takich jak ClassA.variable (nie pytaj mnie dlaczego to nie mój kod).

ClassB import ClassA. Edytowałem wartości ciągu w ClassA i skompilowałem je. Po uruchomieniu ClassB mogłem zobaczyć, że używało starych wartości - a nie nowych wartości. Musiałem przekompilować ClassB, aby użyć nowych wartości z ClassA! (Musiałem przekompilować inne klasy, które importują ClassA!)

Czy to tylko dlatego, że JDK 1.6 lub powinienem wcześniej wiedzieć, aby ponownie skompilować ClassB również! Oświeć mnie. :)

Odpowiedz

23

Jeśli Wartości final zmiennych z klasy ClassA stało się stałe w czasie kompilacji, kompilator mógłby inlined ich do klas używając ClassA zamiast generowania odniesienie run-time. Myślę, że tak właśnie stało się w przypadku, który opisałeś.

przykład:

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

W tym przykładzie, kompilator może zawierać wartość FOO do kodu wygenerowanego przez Consumer zamiast generowania równoważnego odniesienia w czasie wykonywania. Jeśli wartość FOO zmieni się później, będziesz musiał ponownie skompilować Consumer, aby użyć nowej wartości.

Jest to optymalizacja, która ma kilka zalet pod względem wydajności i szybkości kompilowanego programu. Na przykład, inline wartość może umożliwić dalsze optymalizacje w wyrażeniach, które go użyć, na przykład:

int x = Flags.FOO * 10; 

W tym przykładzie inline wartości (tutaj: 1) umożliwia kompilator zauważyć, że marek mnożenia nie ma różnicy i można ją w ogóle pominąć.

+1

więc mówisz, że publiczne statyczne końcowe to czas kompilacji? tego nie wiedziałem. myślał, że to tylko stała i nie może być modyfikowana w czasie wykonywania! dzięki za pomoc. –

+3

Dobry anwser. Jeśli chcesz zobaczyć, że zmienna jest wstawiona, możesz użyć javap, aby zobaczyć, jak klasa została skompilowana, np. "flagi javap -c". –

3

To jest problem z kompatybilnością binarną. Odniesienia do stałych pól są rozwiązywane w czasie kompilacji. Obserwowane zachowanie jest poprawne; jeśli zmienisz wartości w klasie A, będziesz musiał ponownie skompilować klienta (klasa B). Aby uniknąć takich problemów, rozważ dodanie stałych przy użyciu typu wyliczeniowego, wprowadzonego w Javie wersja 5.0.

2

Dlaczego próbujesz skompilować poszczególne zajęcia?

Użyj systemu kompilacji, takiego jak maven lub mrówka, lub po prostu pozwól, aby IDE to zrobił.

Jedyną bezpieczną rzeczą do zrobienia jest rekompilacja każdej java, która zależy od klasy java, która zmieniła się, aż każda klasa, która mogła zostać wykonana, została ponownie skompilowana.

+0

Jedna lub inna klasa może nie być pod twoją kontrolą. – DJClayworth

2

Jeśli nie używasz wartości w przełączniku można to zrobić w zamian:

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

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

następnie kompilator nie będzie już ciężko kodować wartości w innych klas, które z nich korzystają.

2

Załóżmy ClassA wygląda następująco:

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

Jeśli go skompilować, ClassB będzie nadal używać starych wartości. Myślę, że to może zależeć od kompilatora, ale myślę, że jest to typowe zachowanie. Jeśli nie chcesz, aby skompilować ClassB każdym stałym zmian ClassA, musisz zrobić coś takiego:

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 teraz javac nie chce wbudować stałe. Zamiast tego wywoła metodę CONST (int), gdy uruchamiany jest statyczny inicjator ClassA.