Объявление переменной конечной и статической
Этот комментарий был сделан в обзоре кода, и человек, который его сделал, больше не находится в нашей команде.
Любой тип, который должен быть разрешен загрузчиком классов во время выполнения, никогда не должен иметь экземпляры, которые удерживаются ссылками, объявленными как окончательными, так и статическими.
Здесь строка кода:
private final static Logger log = LoggerFactory.getLogger(MyClass.class);
Я знаком с дебатами об объявлении журналов статическими или нестатическими, но этот комментарий кажется более общим. Я не могу найти никаких объяснений, почему статические и конечные плохие. Может кто-нибудь уточнить?
Ответы
Ответ 1
Комментарий, скорее всего, связан с проблемой утечки Classloader (вот хорошая статья).
Вкратце, эта проблема возникает в средах, где загрузчик классов необходимо перезагрузить. Если вы загружаете класс динамически через загрузчик классов, а затем попробуйте перезагрузить загрузчик классов, сохранение статических конечных полей с объектами классов, созданных с помощью этого загрузчика классов, предотвратит разгрузку самого загрузчика классов. Как только это произойдет, вы получите OutOfMemoryError
.
В приведенной выше статье перечислены библиотеки журналов среди главных виновников, которые могут привести к такому поведению, а также меры, которые вы можете предпринять для устранения утечек (например, освобождение классов-загрузчиков явно).
Ответ 2
Строка кода совершенно прекрасна, и нет реальной проблемы, потому что переменная final
и static
.
Возможно, тот, кто сделал этот комментарий, был смущен следующим.
В Java, когда вы создаете переменную public final static
типа int
(например, она также работает с некоторыми другими типами), тогда компилятор может в тех местах, где вы используете эту переменную, заменить фактическое постоянное значение вместо ссылки на переменную. Например, предположим, что у вас есть следующее:
class A {
public final static int VALUE = 3;
}
public class B {
public static void main(String[] args) {
System.out.println(A.VALUE);
}
}
Когда вы скомпилируете и запустите это, он, очевидно, напечатает 3.
Теперь предположим, что вы меняете класс A
и устанавливаете VALUE = 4
. Вы ожидаете, что если вы перекомпилируете класс A
, а затем запустите класс B
(без перекомпиляции класса B
), вы увидите 4
. Но произойдет то, что вы все равно увидите 3
. Это связано с тем, что A.VALUE
в классе B
был заменен фактическим значением константы 3
при компиляции класса B
.
Это оптимизация, которую компилятор Java делает для констант.
Как вы можете видеть, это может вызвать проблемы, если у вас есть такие константы в публичном API ваших классов. Пользователи вашего кода должны будут перекомпилировать свой код, если вы измените значение таких констант.
Но в коде, опубликованном в вашем вопросе, это не проблема, потому что переменная private
.
Подробнее:
Спецификация языка Java 13.4.9