5

This question is strongly related to Change private static final field using Java reflection. There, it was asked, how to change a private static final variable.


However, the answers on that question do not work in Java 12+ as you cannot access private variables of java.lang.reflect.Field using Reflection.

When you try to do it despite that, you will end up with a stack trace like:

Exception java.lang.NoSuchFieldException: modifiers
      at Class.getDeclaredField (Class.java:2412)
      at <your call of Field.class.getDeclaredField("modifiers").setAccessible(true)>

Is there any way to change such a constant in those versions?

I could imagine being possible utilizing JNI/JNA.

dan1st
  • 12,568
  • 8
  • 34
  • 67
  • Seems similar to https://stackoverflow.com/q/55401424 – Jorn Vernee Apr 10 '20 at 15:22
  • 4
    On a general note though, the JVM treats `static final` fields as constants, so there is no guarantee that the updated value will be used in the program, instead of an old value. Better to find another solution to your problem. – Jorn Vernee Apr 10 '20 at 15:24
  • Related: yes, but it is for java 11. This was "fixed" in java 12. – dan1st Apr 10 '20 at 15:24
  • I know that but I am working with objects, not any compile time constant. – dan1st Apr 10 '20 at 15:25
  • 4
    You misunderstand, I'm not just talking about constant expressions, where the value gets inlined by `javac`. I'm talking about the JIT compiler treating _any_ value in a `static final` field as a constant. – Jorn Vernee Apr 10 '20 at 15:26
  • This reference is not called often enough(in my test) to be JITed. – dan1st Apr 10 '20 at 15:28

1 Answers1

15

You can use Unsafe.

public class Example
{
    // javac will inline static final Strings, so let's say it's Object
    private static final Object changeThis = "xxx";

    public static void main(String... args) throws Exception
    {
        final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        final Unsafe unsafe = (Unsafe) unsafeField.get(null);

        System.out.println("before = " + changeThis);

        final Field ourField = Example.class.getDeclaredField("changeThis");
        final Object staticFieldBase = unsafe.staticFieldBase(ourField);
        final long staticFieldOffset = unsafe.staticFieldOffset(ourField);
        unsafe.putObject(staticFieldBase, staticFieldOffset, "it works");

        System.out.println("after = " + changeThis);
    }
}

Result:

before = xxx
after = it works
northpl93
  • 524
  • 4
  • 8