можно ли отключить встраивание javac статических конечных переменных?
статический компилятор Java (javac) вставляет некоторые статические конечные переменные и приносит значения непосредственно в пул констант. Рассмотрим следующий пример. Класс A определяет некоторые константы (открытые статические конечные переменные):
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? Для фона мы изучаем анализ байт-кода для целей зависимости, и это один из немногих случаев где анализ байт-кода не позволяет обнаружить зависимости времени компиляции. Спасибо!
Edit: это досадная проблема, потому что обычно мы не контролируем весь источник (например, сторонние библиотеки, которые определяют константы). Мы заинтересованы в обнаружении этих зависимостей с точки зрения используя константы. Так как ссылка стирается из кода, что использует константы, нет простого способа их обнаружить, за исключением выполнения исходного кода анализ.
9 ответов:
пункт 93 Java Puzzlers (Joshua Bloch) говорит, что вы можете обойти это, не допуская, чтобы конечное значение считалось константой. Например:
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
как случай в блоке коммутатора, это и должен быть указан в качестве постоянного значения.
, чтобы отключить встраивание нужно принять значения не время компиляции константы (термин ПСБ). Вы можете сделать это без использования функций и создания минимального байт-кода с помощью
null
в выражении инициализатора.public static final int INT_VALUE = null!=null?0: 1000;
хотя он очень буквален в своей генерации кода,
javac
следует оптимизировать это, чтобы быть толчком немедленного целого числа, за которым следует хранилище в статическое поле в статическом инициализаторе.
JLS 13.4.9 занимается этим вопросом. Их рекомендация заключается в том, чтобы в основном избегать констант времени компиляции, если значение каким-либо образом может измениться.
(одна из причин для требования встраивания константы заключается в том, что операторы Switch требуются константы на каждый случай, и нет двумя такими постоянными значениями могут быть тот же. Компилятор проверяет наличие дублирование постоянных значений в коммутаторе оператор во время компиляции; класс формат файла не делает символический связь между значениями.)
лучший способ избежать проблем с "непостоянные константы" в широко распространенный код должен объявить как константы времени компиляции только значения что действительно вряд ли когда-либо изменение. Кроме как для истинного математические константы, мы рекомендуем этот исходный код делает очень щадящее использование переменных класса, которые объявлены static и Final. Если только для чтения требуется характер финала, лучше выбор заключается в объявлении частного статический переменная и подходящий аксессуар метод, чтобы получить его значение. Таким образом мы рекомендую:
private static int N; public static int getN() { return N; }
вместо:
public static final int N = ...;
нет никаких проблем с:
public static int N = ...;
Если N не нужно только для чтения.
jmake - это проект с открытым исходным кодом, который утверждает, что выполняет всю работу по отслеживанию зависимостей между файлами Java и постепенной компиляции минимального набора требуемых файлов. Он утверждает, что правильно обрабатывает изменения статических конечных констант, хотя иногда требуется перекомпиляция всего проекта. Он даже обрабатывает изменения с более тонкой детализацией, чем classfiles; если (например) сигнатура метода C. m () изменяется, то он только перекомпилирует классы это на самом деле зависит от m (), а не от всех классов, которые используют C.
отказ от ответственности: у меня нет опыта использования jmake.
Я недавно столкнулся с подобным вопрос, и, как было сказано выше, такая вставка может быть обработана с использованием выражений, не связанных с компиляцией, скажем:
public final class A { public static final int INT_VALUE = constOf(1000); public static final String STRING_VALUE = constOf("foo"); }
здесь
constOf
семейство методов просто:// @formatter:off public static boolean constOf(final boolean value) { return value; } public static byte constOf(final byte value) { return value; } public static short constOf(final short value) { return value; } public static int constOf(final int value) { return value; } public static long constOf(final long value) { return value; } public static float constOf(final float value) { return value; } public static double constOf(final double value) { return value; } public static char constOf(final char value) { return value; } public static <T> T constOf(final T value) { return value; } // @formatter:on
Это немного короче, чем другие предложения, как
Integer.valueOf(1000).intValue()
илиnull!=null?0: 1000
Я думаю, что это серьезные ошибка. Java-это не C / C++. Есть принцип (или нет) "Компилируй один раз, беги везде".
в этом случае, когда класс A изменяется. Любые классы, ссылающиеся на A. CONST_VALUE, должны быть повторно скомпилированы, и они вряд ли знают, изменен ли класс A.
переписать класс A как:
public class A { public static final int INT_VALUE; public static final String STRING_VALUE; static { INT_VALUE = 1000; STRING_VALUE = "foo"; } }
Я чувствую, что java тесно зависит от динамической компиляции, и она не делает никакой причудливой логики компиляции, как C++.
вы можете попробовать некоторые параметры с JIT-компиляторами, которые оптимизируют время выполнения, которые могут иметь некоторые параметры для отключения/включения этого.
по умолчанию javac вы не можете получить эту опцию. вы должны использовать 1. некоторые типы графов зависимостей, такие как расширения или реализации 2. использование метода на основе ссылки.
- s