JDK版本:1.8.0_291
目標類:
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);
}
}
測驗代碼:
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();
}
}
測驗結果:
nameStringBuilder: 2
nameString: 1
結論:
StringBuilder 欄位可以用反射修改,但 String 欄位不能修改。
問題:
為什么String不能用反射修改?
有什么方法可以修改private final static String上面 StringBuilder 案例中的值嗎?
uj5u.com熱心網友回復:
Java 編譯器(根據 Java 語言規范)需要行內常量變數(請參閱https://docs.oracle.com/javase/specs/jls/se17/html/jls-13.html#jls-13.1-110- C用于當前版本,但相同的要求已經在J??LS for Java 1.6中)。
您可以通過制作nameString所謂的“空白最終”(https://docs.oracle.com/javase/specs/jls/se6/html/typesValues.html#10931)來解決此問題:
private static final String nameString;
static {
nameString = "1";
}
這解決了你當前的問題,但如果你想升級你的 Java 版本,它會給你帶來麻煩。Java 12 及更高版本不再允許設定類的modifier欄位Field。如果您的代碼依賴于這個 hack,它將永遠被鎖定在 Java 11 或更低版本。
更新到上一段:在這種情況下(僅運行一些測驗)它可能會解決您的問題 ,但是:JVM 可以自由優化對該欄位的訪問(即使在一個空白的最終欄位的情況下,它也被宣告為final并且JIT 優化器可以進行非最終欄位無法進行的優化)
要顯示問題:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Main {
public static void main (String[] args) throws Exception {
System.out.println("Before:");
System.out.println("Field1: " Class2.getField1());
System.out.println("Field2: " Class2.getField2());
System.out.println("Field3: " Class2.getField3());
System.out.println("Field3: " Class2.getField3());
System.out.println();
Class c = Class2.class;
Field f1 = c.getDeclaredField("field1");
Field f2 = c.getDeclaredField("field2");
Field f3 = c.getDeclaredField("field3");
changeField(f1);
changeField(f2);
changeField(f3);
System.out.println("After:");
System.out.println("Field1: " Class2.getField1());
System.out.println("Field2: " Class2.getField2());
System.out.println("Field3: " Class2.getField3());
}
private static void changeField(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, "changed");
}
}
class Class2 {
private static final String field1 = "no change"; // constant variable, read access must be inlined
private static final String field2; // blank final
private static final String field3; // blank final
static {
field2 = "no change";
field3 = "no change";
}
public static String getField1() { return field1; }
public static String getField2() { return field2; }
public static String getField3() {
for (int i = 0; i < 1_000_000; i ) {
if (!field3.equals("no change")) {
return field3;
}
}
return field3;
}
}
它會列印出來
Before: Field1: no change Field2: no change Field3: no change Field3: no change After: Field1: no change Field2: changed Field3: no change
因此,JIT(在運行時)更改了代碼方法getField3,不再訪問該欄位field3,而是直接加載常量“無變化”。由于field3被宣告為 final,這是一個完全合法的優化。
如果您嘗試使用 Java 17 運行它,它將引發例外:
Exception in thread "main" java.lang.NoSuchFieldException: modifiers at java.base/java.lang.Class.getDeclaredField(Class.java:2610) at Main.changeField(Main.java:xx) at Main.main(Main.java:xx)
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/494634.html
上一篇:用Java建模錢包邏輯
