Все ли константы времени компиляции встроены?

Скажем, у меня есть такой класс:

class ApplicationDefs{
public static final String configOption1 = "some option";
public static final String configOption2 = "some other option";
public static final String configOption3 = "yet another option";
}

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

Есть ли возможность отключить встраивание констант времени компиляции?


person Saravanan M    schedule 18.12.2008    source источник


Ответы (6)


Вы можете использовать String.intern () для получения желаемого эффекта, но следует прокомментировать свой код, потому что не многие люди знают об этом. т.е.

public static final String configOption1 = "some option".intern();

Это предотвратит встроенное время компиляции. Поскольку это относится к той же самой строке, которую компилятор поместит в perm, вы не создаете ничего лишнего.

В качестве альтернативы вы всегда можете сделать

public static final String configOption1 = "some option".toString();

однако это не будет использовать скомпилированную внутреннюю строку, она создаст новую на старом поколении. Ничего особенного, и, возможно, будет легче читать. В любом случае, поскольку это немного странно, вы должны прокомментировать код, чтобы проинформировать тех, кто его обслуживает, о том, что вы делаете.

Изменить: нашел еще одну ссылку SO, которая дает ссылки на JLS, для получения дополнительной информации об этом. Когда использовать intern () для строковых литералов

person reccles    schedule 25.02.2010
comment
Код aString.toString() возвращает сам aString, а не новый, поэтому он так же действителен, как intern() для целей этого вопроса. Новая копия String будет создана редко используемым конструктором new String(aString). - person Christian Semrau; 12.04.2012
comment
intern() строковые литералы бесполезны. Они уже интернированы. См. здесь. - person ceving; 30.09.2014
comment
@ceving - в данном случае это не бесполезно, потому что предотвращает встраивание configOption1 и т. д. в другие классы. В противном случае, если какой-либо другой класс ссылается на configOption1, а затем вы измените константу и не перекомпилируете другой класс, поведение будет не таким, как вы ожидаете (или, вероятно, хотите). В этом весь смысл вопроса OP. - person Ted Hopp; 03.08.2015

Нет, боюсь, это часть JLS. Это вкратце затронуто в Java Puzzlers, но у меня нет своей копии под рукой.

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

Ссылка: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#5313

person GaryF    schedule 18.12.2008
comment
Ага. Если вы декомпилируете свой код с помощью JAD, вы обнаружите, что все константы времени компиляции были встроены. Я бы проголосовал за файл свойств или описанные статические методы. - person Dave Ray; 18.12.2008

Нет. Вы можете заменить их вызовом статического метода, например:

class ApplicationDefs {

    public static String configOption1() { return "some option"; }

}

Конечно, это некрасиво, но вам подойдет. :)

person Bombe    schedule 18.12.2008
comment
Это решение обеспечивает поведение, идентичное тому, что было предложено в моем ответе. Не вижу смысла оборачивать константу методом. - person Yuval Adam; 18.12.2008
comment
@Yuval - За исключением того, что в предложенном вами ответе переменные могут быть изменены внешними классами. - person Greg; 13.01.2011

Фактически, если вы удалите ключевое слово final, константы перестанут быть константами времени компиляции, и тогда ваша конфигурация будет работать как вы хотите.

Однако настоятельно рекомендуется, что если это действительно какая-то конфигурация, которую вы пытаетесь выполнить, вам следует перейти к более управляемому способу, чем константы в каком-либо файле класса.

person Yuval Adam    schedule 18.12.2008
comment
Фактически, если вы удалите ключевое слово final, константы перестанут быть константами, они будут переменными. - person whiskeysierra; 27.02.2010

Вы можете запретить встраивание, сделав свои постоянные постоянные времени, не связанные с компиляцией ...

Например, null не является постоянной времени компиляции. Любое выражение, включающее некоторую константу времени компиляции, не является константой времени компиляции, хотя javac может выполнять сворачивание констант внутри модуля компиляции.

public static final String configOption1 = null!=null?"": "some option";
person Tom Hawtin - tackline    schedule 18.12.2008

Здесь нет ничего, что говорило бы, что эти значения должны быть встроены. Вы просто объявляете некоторых public, static участников. Эти другие классы используют значения этих членов. Встраивание не требуется. Даже ключевое слово final

Но по соображениям производительности некоторые JVM могут встроить эти значения в другие классы. Это оптимизация. Никакая оптимизация не должна изменять поведение программы. Поэтому, если вы измените определение этих членов, JVM должна отключить предыдущие значения.

Вот почему нет возможности отключить встраивание. Либо JVM не встроен, и проблем нет, либо, если он встроен, JVM гарантирует отключение.

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

person Pierre    schedule 18.12.2008
comment
Я не думаю, что это так: JLS указывает, что константы времени компиляции всегда встроены. Более того, в случае строковых констант времени компиляции они интернированы, и все использования относятся к одному и тому же экземпляру. - person GaryF; 18.12.2008
comment
Было бы неплохо знать, где JLS требует, чтобы константа времени компиляции была встроена во время компиляции. - person Pierre; 18.12.2008