Статический компилятор Java (javac) включает некоторые статические конечные переменные и переносит значения непосредственно в пул констант. Рассмотрим следующий пример. Класс A определяет некоторые константы (public static final) переменные):
public class A {
public static final int INT_VALUE = 1000;
public static final String STRING_VALUE = "foo";
}
Класс B использует следующие константы:
public class B {
public static void main(String[] args) {
int i = A.INT_VALUE;
System.out.println(i);
String s = A.STRING_VALUE;
System.out.println(s);
}
}
Когда вы компилируете класс B, javac получает значения этих констант из класса A и вставляет эти значения в B.class. В результате зависимость B должна была A во время компиляции стирается из байт-кода. Это довольно своеобразное поведение, потому что вы запекаете значения этих констант во время компиляции . И вы могли бы подумать, что это одна из самых простых вещей, которую JIT-компилятор может делать во время выполнения.
Есть ли какой-либо способ или какой-либо скрытый параметр компилятора, который позволяет отключить это встроенное поведение javac? Для справки, мы рассматриваем анализ байт-кода для целей зависимости, и это один из немногих случаев, когда анализ байт-кода не может обнаружить зависимости во время компиляции. Спасибо!
Редактировать : это неприятная проблема, потому что обычно мы не контролируем весь источник (например, сторонние библиотеки, которые определяют константы). Мы заинтересованы в обнаружении этих зависимостей с точки зрения с использованием констант. Поскольку ссылка стирается из кода, который использует константы, нет простого способа их обнаружить, если не выполнять анализ исходного кода.
В правиле 93 Java Puzzlers (Джошуа Блох) говорится, что вы можете обойти это, не допуская, чтобы конечное значение считалось константой. Например:
public class A {
public static final int INT_VALUE = Integer.valueOf(1000).intValue();
public static final String STRING_VALUE = "foo".toString();
}
Конечно, все это не имеет значения, если у вас нет доступа к коду, определяющему константы.
Я так не считаю. Простейшим обходным решением было бы представить их как свойства, а не поля:
public class A {
private static final int INT_VALUE = 1000;
private static final String STRING_VALUE = "foo";
public static int getIntValue() {
return INT_VALUE;
}
public static String getStringValue() {
return STRING_VALUE;
}
}
Не забывайте, что в некоторых случаях инлайнинг необходим для использования значения - например, если вы хотите использовать INT_VALUE
в качестве case в блоке switch, то должен быть указан как постоянное значение.
JLS 13.4.9 решает эту проблему. Их рекомендация - в основном избегать констант времени компиляции, если значение каким-либо образом может измениться.
(Одна из причин, по которой требуется встраивание константы - это операторы переключения требовать константы для каждого случая, и нет два таких постоянных значения могут быть такой же. Компилятор проверяет наличие повторяющиеся постоянные значения в переключателе заявление во время компиляции; класс формат файла не является символическим связь значений регистра.)
Лучший способ избежать проблем с «непостоянные константы» в широко распространенный код должен объявить только значения констант времени компиляции которые действительно вряд ли когда-либо изменять. Кроме истины математические константы, мы рекомендуем этот исходный код очень экономно используется объявленных переменных класса статический и окончательный. Если только для чтения характер финала обязателен, лучше выбор - объявить частный статический переменная и подходящий аксессуар метод, чтобы получить его значение.Таким образом, мы рекомендую:
private static int N; общедоступный статический int getN () {вернуть N; }
, а не:
public static final int N = ...;
Нет проблем с:
public static int N = ...;
, если N не должен быть доступен только для чтения.
Чтобы остановить встраивание, вам нужно сделать значения неконстантами времени компиляции (термин JLS). Вы можете сделать это без использования функций и создания минимального байт-кода, используя null
в выражении инициализатора.
public static final int INT_VALUE = null!=null?0: 1000;
Несмотря на то, что код генерируется очень буквально, javac
должен оптимизировать его, чтобы он был немедленным отправлением целого числа с последующим сохранением в статическое поле в статическом инициализаторе.
Я чувствую, что java сильно полагается на динамическую компиляцию и не делает никакой причудливой логики компиляции, как в C++.
Вы можете попробовать некоторые варианты с JIT компиляторами, которые делают оптимизацию во время выполнения, которые могут иметь некоторые опции для отключения/включения этого.
В стандартном javac вы не сможете воспользоваться этой опцией. 1. какой-то тип графа зависимостей, например extends или implements 2. использовать связывание на основе методов.
-s