Изменение конечных переменных посредством отражения, почему разница между статической и нестатической конечной переменной

Пожалуйста, обратитесь к приведенному ниже коду. Когда я запускаю код, я могу изменить значение конечной нестатической переменной. Но если я попытаюсь изменить значение окончательной статической переменной, то получится 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.
Другие вопросы по тегам