Изменение конечных переменных посредством отражения, почему разница между статической и нестатической конечной переменной
Пожалуйста, обратитесь к приведенному ниже коду. Когда я запускаю код, я могу изменить значение конечной нестатической переменной. Но если я попытаюсь изменить значение окончательной статической переменной, то получится java.lang.IllegalAccessException
,
Мой вопрос: почему он не генерирует исключение в случае нестатической конечной переменной также или наоборот. Почему разница?
import java.lang.reflect.Field;
import java.util.Random;
public class FinalReflection {
final static int stmark = computeRandom();
final int inmark = computeRandom();
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
FinalReflection obj = new FinalReflection();
System.out.println(FinalReflection.stmark);
System.out.println(obj.inmark);
Field staticFinalField = FinalReflection.class.getDeclaredField("stmark");
Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark");
staticFinalField.setAccessible(true);
instanceFinalField.setAccessible(true);
instanceFinalField.set(obj, 100);
System.out.println(obj.inmark);
staticFinalField.set(FinalReflection.class, 101);
System.out.println(FinalReflection.stmark);
}
private static int computeRandom() {
return new Random().nextInt(5);
}
}
3 ответа
FinalReflectionobj = new FinalReflection();
System.out.println(FinalReflection.stmark);
System.out.println(obj.inmark);
Field staticFinalField = FinalReflection.class.getDeclaredField("stmark");
Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark");
staticFinalField.setAccessible(true);
instanceFinalField.setAccessible(true);
//EXTRA CODE
//Modify the final using reflection
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(staticFinalField, staticFinalField.getModifiers() & ~Modifier.FINAL);
instanceFinalField.set(obj, 100);
System.out.println(obj.inmark);
staticFinalField.set(FinalReflection.class, 101);
System.out.println(FinalReflection.stmark);
Это решение не обходится без некоторых недостатков, оно может работать не во всех случаях:
В случае, если final
поле инициализируется константой времени компиляции в объявлении поля, изменяется на final
поле может быть невидимым, так как использование этого последнего поля заменяется во время компиляции константой времени компиляции.
Другая проблема заключается в том, что спецификация допускает агрессивную оптимизацию final
поля. В пределах потока допустимо изменить порядок чтения final
поле с этими модификациями final
поля, которые не имеют места в конструкторе. Подробнее об этом также объясняется в этом аналогичном вопросе.
Javadoc ясен:
Если базовое поле является окончательным, метод генерирует исключение IllegalAccessException, если setAccessible(true) не удалось для этого объекта поля и поле не является статическим.
С точки зрения JLS точное поведение того, как должно работать отражение, не указано, но в JLS 17.5.4:
Обычно поле, которое является окончательным и статическим, не может быть изменено.
Одним из обходных путей является удаление окончательного модификатора путем отражения.
Для финала, ему могут быть присвоены разные значения во время выполнения при инициализации.
Class Test{
public final int a;
}
Test t1 = new Test();
t1.a = 10;
Test t2 = new Test();
t1.a = 20;
Таким образом, каждый экземпляр имеет различное значение поля a.
Для статического final все экземпляры имеют одинаковое значение и не могут быть изменены после первой инициализации.
Class TestStatic{
public static final int a;
}
Test t1 = new Test();
t1.a = 10;
Test t2 = new Test();
t1.a = 20; // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION.