Измените частное статическое конечное поле с помощью отражения Java
У меня есть класс с 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);
Переведено автоматически
Ответ 1
Предполагая, что ничто SecurityManager
не мешает вам сделать это, вы можете использовать setAccessible
для обхода private
и сброса модификатора, чтобы избавиться от final
, и фактически изменить private static final
поле.
Вот пример:
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
Предполагая, что no SecurityException
не выдается, печатается приведенный выше код "Everything is true"
.
На самом деле здесь делается следующее:
- Примитивные
boolean
значенияtrue
иfalse
вmain
автоматически переносятся в ссылочный типBoolean
"константы"Boolean.TRUE
иBoolean.FALSE
- Отражение используется для изменения
public static final Boolean.FALSE
ссылки наBoolean
, на которую ссылаетсяBoolean.TRUE
- В результате, впоследствии, когда a
false
автоматически добавляется вBoolean.FALSE
, оно ссылается на то жеBoolean
, что и то, на которое ссылаетсяBoolean.TRUE
- Все, что было,
"false"
теперь есть"true"
Вопросы по теме
- Использование отражения для изменения
static final File.separatorChar
для модульного тестирования - Как ограничить setAccessible только “законным” использованием?
- Есть примеры работы с
Integer
кэшем, изменения aString
и т. Д
- Есть примеры работы с
Предостережения
Всякий раз, когда вы делаете что-то подобное, следует соблюдать крайнюю осторожность. Это может не сработать, потому что SecurityManager
может присутствовать, но даже если этого не произойдет, в зависимости от шаблона использования, это может сработать, а может и не сработать.
JLS 17.5.3 Последующее изменение конечных полей
В некоторых случаях, таких как десериализация, системе потребуется изменить
final
поля объекта после построения.final
поля могут быть изменены с помощью отражения и других средств, зависящих от реализации. Единственный шаблон, в котором это имеет разумную семантику, - это тот, в котором создается объект, а затемfinal
поля объекта обновляются. Объект не должен быть виден другим потокам, аfinal
поля не должны считываться, пока не будут завершены все обновленияfinal
полей объекта. Зависанияfinal
поля происходят как в конце конструктора, в котором заданоfinal
поле, так и сразу после каждой модификацииfinal
поля с помощью отражения или другого специального механизма.Даже в этом случае существует ряд сложностей. Если
final
поле инициализировано константой времени компиляции в объявлении поля, изменения вfinal
поле могут не наблюдаться, поскольку использование этогоfinal
поля заменяется во время компиляции константой времени компиляции.Другая проблема заключается в том, что спецификация допускает агрессивную оптимизацию
final
полей. В потоке допустимо изменять порядок чтенияfinal
поля с теми модификациями конечного поля, которые не выполняются в конструкторе.
Смотрите также
- Постоянное выражение JLS 15.28
- Маловероятно, что этот метод работает с примитивом
private static final boolean
, потому что он может быть встроен как константа времени компиляции, и, следовательно, "новое" значение может быть не наблюдаемым
- Маловероятно, что этот метод работает с примитивом
Приложение: О побитовых манипуляциях
По сути,
field.getModifiers() & ~Modifier.FINAL
отключает бит, соответствующий Modifier.FINAL
from field.getModifiers()
. &
является побитовым-и, и ~
является побитовым дополнением.
Смотрите также
Запомните постоянные выражения
Все еще не можете решить это?, впали в депрессию, как я сделал для этого? Ваш код выглядит так?
public class A {
private final String myVar = "Some Value";
}
Читая комментарии к этому ответу, особенно от @Pshemo, я вспомнил, что постоянные выражения обрабатываются по-разному, поэтому изменить его будет невозможно. Следовательно, вам нужно будет изменить свой код, чтобы он выглядел следующим образом:
public class A {
private final String myVar;
private A() {
myVar = "Some Value";
}
}
если вы не являетесь владельцем класса... Я чувствую вас!
Для получения более подробной информации о причинах такого поведения прочтите это?
Ответ 2
Если значение, присвоенное static final boolean
полю, известно во время компиляции, это константа. Поля примитивного или String
типа могут быть константами времени компиляции. Константа будет встроена в любой код, который ссылается на поле. Поскольку поле фактически не считывается во время выполнения, его изменение не будет иметь никакого эффекта.
В спецификации языка Java сказано следующее:
Если поле является постоянной переменной (§ 4.12.4), то удаление ключевого слова final или изменение его значения не нарушит совместимость с ранее существующими двоичными файлами, поскольку они не будут запускаться, но они не увидят никакого нового значения для использования поля, если они не будут перекомпилированы. Это верно, даже если само использование не является постоянным выражением во время компиляции (§15.28)
Вот пример:
class Flag {
static final boolean FLAG = true;
}
class Checker {
public static void main(String... argv) {
System.out.println(Flag.FLAG);
}
}
Если вы декомпилируете Checker
, вы увидите, что вместо ссылки Flag.FLAG
код просто помещает значение 1 (true
) в стек (инструкция № 3).
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #3; //Method java/io/PrintStream.println:(Z)V
7: return
Ответ 3
Небольшое любопытство из спецификации языка Java, глава 17, раздел 17.5.4 "Поля, защищенные от записи":
Обычно поле, которое является конечным и статическим, не может быть изменено. Однако, System.in System.out и System.err являются статическими конечными полями, которые по устаревшим причинам должны быть разрешены для изменения методами System.setIn, System.setOut и System.setErr. Мы называем эти поля защищенными от записи, чтобы отличать их от обычных конечных полей.
Источник: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
Ответ 4
Я также интегрировал его с библиотекой joor
Просто используйте
Reflect.on(yourObject).set("finalFieldName", finalFieldValue);
Также я исправил проблему, с override
которой, похоже, не было в предыдущих решениях.
Однако используйте это очень осторожно, только когда нет другого хорошего решения.