2009-11-07 3 views
18

Я случайно столкнулся с кодом Java на своем рабочем месте. Вот сценарий: Есть 2 класса - ClassA и ClassB.публичная статическая конечная переменная в импортированном классе java

ClassA не содержит ничего, кроме 4 общедоступных статических окончательных значений строки внутри него. Его цель - использовать такие значения, как ClassA.variable (не спрашивайте меня, почему, это не мой код).

ClassB импорт ClassA. Я отредактировал строковые значения в ClassA и скомпилировал его. Когда я запустил ClassB, я видел, что он использовал старые значения, а не новые значения. Мне пришлось перекомпилировать ClassB, чтобы использовать новые значения от ClassA! (Мне пришлось перекомпилировать другие классы, которые импортируют ClassA!)

Это просто из-за JDK 1.6 или я должен был раньше знать, чтобы перекомпилировать ClassB! Просветите меня. :)

ответ

23

Если значения final переменных из класса ClassA случаются во время компиляции константы, компилятор мог бы встраиваются их в классы с использованием 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

Итак, вы говорите, что public static final - это константа времени компиляции? не знал этого. думал, что он просто постоянен и не может быть изменен во время выполнения! Спасибо за вашу помощь. –

+3

Хороший anwser. Если вы хотите видеть, что переменная встраивается, вы можете использовать javap, чтобы увидеть, как скомпилирован класс, например. «javap -c Flags». –

3

Это проблема совместимости двоичных файлов. Ссылки на постоянные поля разрешаются во время компиляции. Поведение, которое вы видите, является правильным; если вы измените значения в классе A, вам придется повторно скомпилировать клиента (класс B). Чтобы избежать таких проблем, рассмотрите возможность добавления констант с использованием типа enum, представленного в версии 5.0 Java.

2

Почему вы пытаетесь скомпилировать классы отдельно?

Используйте систему сборки, такую ​​как maven или ant или просто позволяйте вашей среде IDE делать это.

Единственное, что нужно сделать, это перекомпилировать каждую java, которая зависит от класса java, который изменился, до тех пор, пока каждый класс, который может быть выполнен, был повторно скомпилирован.

+0

Один или другой класс не может находиться под вашим контролем. – DJClayworth

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

Becuase Теперь Javac не желает встраивать константы. Вместо этого он будет вызывать метод CONST (int), когда запускается статический инициализатор ClassA.

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