У меня есть класс с private static final
полем, которое, к сожалению, мне нужно изменить во время выполнения.
Используя отражение, я получаю эту ошибку: java.lang.IllegalAccessException: Can not set static final boolean field
Есть ли способ изменить значение?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
System.out/in/err
настолько «особенный», что в модели памяти Java следует особо упоминать их. Это не примеры, которым нужно следовать.Ответы:
Предполагая, что нет
SecurityManager
мешает вам сделать это, вы можете использовать,setAccessible
чтобы обойтиprivate
и сбросить модификатор, чтобы избавиться от негоfinal
, и фактически изменитьprivate static final
поле.Вот пример:
Предполагая, что нет
SecurityException
, вышеприведенный код печатает"Everything is true"
.То, что на самом деле сделано здесь, выглядит следующим образом:
boolean
значенияtrue
иfalse
вmain
автоматически помещаются в ссылочный типBoolean
«константы»Boolean.TRUE
иBoolean.FALSE
public static final Boolean.FALSE
чтобы ссылаться наBoolean
упомянутыйBoolean.TRUE
false
автоматически помещается в ящикBoolean.FALSE
, он ссылается на тот,Boolean
на который ссылаетсяBoolean.TRUE
"false"
сейчас,"true"
Смежные вопросы
static final File.separatorChar
для модульного тестированияInteger
кешем, мутацией иString
т. Д.Предостережения
Необходимо проявлять крайнюю осторожность, когда вы делаете что-то подобное. Он может не работать, потому что
SecurityManager
может присутствовать, но даже если это не так, в зависимости от модели использования, он может работать или не работать.Смотрите также
private static final boolean
, потому что он встроен как константа времени компиляции и, таким образом, «новое» значение может быть не наблюдаемымПриложение: О побитовой манипуляции
По существу,
выключает бит, соответствующий
Modifier.FINAL
сfield.getModifiers()
.&
является побитовым и~
является побитовым дополнением.Смотрите также
Помните постоянные выражения
Все еще не в состоянии решить это?, Впал в депрессию, как я сделал для этого? Ваш код выглядит так?
Прочитав комментарии к этому ответу, особенно @Pshemo, он напомнил мне, что выражения констант обрабатываются по-разному, поэтому изменить их будет невозможно . Следовательно, вам нужно будет изменить свой код, чтобы он выглядел так:
если ты не владелец класса ... я тебя чувствую!
Для более подробной информации о том, почему это поведение читать это ?
источник
getDeclaredField()
вместоgetField()
целевого классаfinal String myConstant = "x";
и потерпит неудачу: помните, что константы времени компиляции будут встроены компилятором, поэтому, когда вы будете писать код так, какSystem.out.println(myConstant);
он будет скомпилирован,System.out.println("x");
потому что компилятор знает значение константы во время компиляции. Чтобы избавиться от этой проблемы, вам нужно инициализировать ваши константы во время выполнения, какfinal String myConstant = new String("x");
. Кроме того, в случае примитивов нравитсяfinal int myField = 11
использоватьfinal int myField = new Integer(11);
илиfinal Integer myField = 11;
Если значение, назначенное
static final boolean
полю, известно во время компиляции, оно является константой. Поля примитива илиString
типа могут быть константами времени компиляции. Константа будет встроена в любой код, который ссылается на поле. Поскольку поле на самом деле не читается во время выполнения, его изменение не будет иметь никакого эффекта.Спецификация языка Java говорит это:
Вот пример:
Если вы декомпилируете
Checker
, вы увидите, что вместо ссылкиFlag.FLAG
код просто помещает значение 1 (true
) в стек (инструкция # 3).источник
public static final Boolean FALSE = new Boolean(false)
неpublic static final boolean FALSE = false
Немного любопытства из Спецификации языка Java, глава 17, раздел 17.5.4 «Поля, защищенные от записи»:
Источник: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
источник
Я также интегрировал его с библиотекой Joor
Просто используйте
Также я исправил проблему, из-за
override
которой предыдущие решения, похоже, отсутствуют. Однако используйте это очень осторожно, только когда нет другого хорошего решения.источник
Наряду с ответом с наивысшим рейтингом вы можете использовать немного более простой подход. У
FieldUtils
класса Apache Commons уже есть особый метод, который может делать вещи. Пожалуйста, посмотрите наFieldUtils.removeFinalModifier
метод. Вы должны указать экземпляр целевого поля и флаг форсирования доступности (если вы играете с закрытыми полями). Более подробную информацию вы можете найти здесь .источник
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
В случае присутствия диспетчера безопасности, можно использовать
AccessController.doPrivileged
Взяв тот же пример из принятого ответа выше:
В лямбда-выражении,
AccessController.doPrivileged
можно упростить до:источник
Принятый ответ работал для меня, пока не был развернут на JDK 1.8u91. Затем я понял, что это не удалось в
field.set(null, newValue);
строке, когда я прочитал значение через отражение перед вызовомsetFinalStatic
метода.Вероятно, чтение вызвало несколько иную настройку внутренних средств отражения Java (а именно,
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
в случае неудачи, а не в случаеsun.reflect.UnsafeStaticObjectFieldAccessorImpl
успеха), но я не стал это подробно развивать.Так как мне нужно было временно установить новое значение на основе старого значения, а затем вернуть старое значение обратно, я немного изменил сигнатуру, чтобы обеспечить функцию вычисления извне, а также вернуть старое значение:
Однако для общего случая этого будет недостаточно.
источник
Даже несмотря на то, что
final
что поле может быть изменено вне статического инициализатора и (по крайней мере, JVM HotSpot) прекрасно выполнит байт-код.Проблема в том, что компилятор Java не позволяет этого, но это можно легко обойти, используя
objectweb.asm
. Вот совершенно правильный файл классов, который проходит проверку байт-кода и успешно загружается и инициализируется в JVM HotSpot OpenJDK12:В Java класс выглядит примерно следующим образом:
который не может быть скомпилирован с
javac
, но может быть загружен и выполнен JVM.JVM HotSpot имеет особый подход к таким классам в том смысле, что он предотвращает участие таких «констант» в постоянном сворачивании. Эта проверка выполняется на этапе перезаписи байт-кода при инициализации класса :
Единственное ограничение, которое проверяет JVM HotSpot, заключается в том, что
final
поле не должно изменяться вне класса, в которомfinal
оно объявлено.источник
Только что увидел этот вопрос на одном из вопросов интервью, если возможно изменить окончательную переменную с отражением или во время выполнения. Стало действительно интересно, так что я стал с чем:
Какой-то простой класс с конечной строковой переменной. Так в основном классе import java.lang.reflect.Field;
Вывод будет следующим:
Согласно документации https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
источник
static
последнего поля, поэтому этот код не работает.setAccessible(true)
работает только для установки полей окончательного экземпляра.Если ваше поле просто личное, вы можете сделать это:
и бросить / обработать NoSuchFieldException
источник
Весь смысл
final
поля в том, что его нельзя переназначить после установки. JVM использует эту гарантию для поддержания согласованности в различных местах (например, внутренние классы, ссылающиеся на внешние переменные). Так что нет. Быть способным сделать это сломало бы JVM!Решение не в том, чтобы объявить это
final
в первую очередь.источник
final
играет особую роль в многопоточном выполнении - изменениеfinal
значений также нарушит модель памяти Java.final
не должно быть объявленоstatic
.