JDK version: 1.8.0_291
Target class:
package reflectionStackoverflow.test;
public class ClassA {
private final static StringBuilder nameStringBuilder = new StringBuilder("1");
private final static String nameString = "1";
public static void printNameStringBuild() {
System.out.println("nameStringBuilder: " nameStringBuilder);
}
public static void printNameString() {
System.out.println("nameString: " nameString);
}
}
Test Code:
package reflectionStackoverflow.test;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectionTest {
@Test
public void test() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Class c = ClassA.class;
Field nameStringBuilder = c.getDeclaredField("nameStringBuilder");
nameStringBuilder.setAccessible(true);
Field nameStringBuilderModifiers = nameStringBuilder.getClass().getDeclaredField("modifiers");
nameStringBuilderModifiers.setAccessible(true);
nameStringBuilderModifiers.setInt(nameStringBuilder, nameStringBuilder.getModifiers() & ~Modifier.FINAL);
nameStringBuilder.set(c, new StringBuilder("2"));
nameStringBuilderModifiers.setInt(nameStringBuilder, nameStringBuilder.getModifiers() & ~Modifier.FINAL);
Field nameString = c.getDeclaredField("nameString");
nameString.setAccessible(true);
Field nameStringModifiers = nameString.getClass().getDeclaredField("modifiers");
nameStringModifiers.setAccessible(true);
nameStringModifiers.setInt(nameString, nameString.getModifiers() & ~Modifier.FINAL);
nameString.set(c, "2");
nameStringModifiers.setInt(nameString, nameString.getModifiers() & ~Modifier.FINAL);
ClassA.printNameStringBuild();
ClassA.printNameString();
}
}
Test result:
nameStringBuilder: 2
nameString: 1
Conclusion:
StringBuilder field can be modified with reflection, but String field can not be modified.
Question:
Why String can not be modified with reflection?
Is there any way to modify a private final static String
value like above StringBuilder case?
CodePudding user response:
The Java compiler is required (by the Java Language Specification) to inline constant variables (see https://docs.oracle.com/javase/specs/jls/se17/html/jls-13.html#jls-13.1-110-C for the current version, but the same requirement was already in the JLS for Java 1.6).
You can work around this by making nameString
a so called "blank final" (https://docs.oracle.com/javase/specs/jls/se6/html/typesValues.html#10931):
private static final String nameString;
static {
nameString = "1";
}
This solves your current problem, but it will get you into trouble if you ever want to upgrade your Java version. Java 12 and later do no longer allow setting the modifier
field of the Field
class. If your code depends on this hack it will forever be locked to Java versions 11 or lower.
CodePudding user response:
This won't be possible anymore due to the reimplementation of the core reflection over invokedynamic
and MethodHandles
as part of JEP-416