Change static final field in java 12+
Asked Answered
P

1

6

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.

Pianoforte answered 10/4, 2020 at 13:58 Comment(6)
Seems similar to https://mcmap.net/q/1630986/-how-to-rewrite-a-static-final-field-in-jdk11Odisodium
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.Odisodium
Related: yes, but it is for java 11. This was "fixed" in java 12.Pianoforte
I know that but I am working with objects, not any compile time constant.Pianoforte
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.Odisodium
This reference is not called often enough(in my test) to be JITed.Pianoforte
S
23

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
Syrupy answered 11/4, 2020 at 1:4 Comment(4)
Tested with jdk15 and jdk16 and it works!Curettage
Works with jdk11 and jdk17, tested with some online playgroundAstoria
I get "Symbol is declared in module 'java.base' which does not export package 'jdk.internal.misc'" with Kotlin on java17Juggins
Tested with jdk21 and it works !Zoie

© 2022 - 2024 — McMap. All rights reserved.