Вопрос-ответ

Change private static final field using Java reflection

Измените частное статическое конечное поле с помощью отражения 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"

Вопросы по теме


Предостережения

Всякий раз, когда вы делаете что-то подобное, следует соблюдать крайнюю осторожность. Это может не сработать, потому что 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 которой, похоже, не было в предыдущих решениях.
Однако используйте это очень осторожно, только когда нет другого хорошего решения.

java reflection