低版本修改final和static

参考文章:https://www.cnblogs.com/noKing/p/9038234.html

修改static变量

这里和正常修改普通变量一样都是没问题的

import java.lang.reflect.Field;

public class Test1 {
public static String str="ceshi";
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;
//修改static变量
Field str1 = test1Class.getDeclaredField("str");
str1.setAccessible(true);
str1.set(test1,"ceshi1");
System.out.println(Test1.str);
}
}

image-20241004100135810

修改final变量

这里有点不同

我们修改StringBuilder变量

package org.clown;



import java.lang.reflect.Field;

public class Test1 {
private final StringBuilder name = new StringBuilder("default2");
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;
//修改final的变量
Field name1 = test1Class.getDeclaredField("name");
name1.setAccessible(true);
name1.set(test1,new StringBuilder("ceshi3"));
System.out.println(test1.name);
}
}

image-20241004100649459

看到是可以修改成功的

但是如果我们修改String变量会发现修改不成功,这是因为String类型的final变量,在优化时会将其变成常量,比如下面的System.out.println(test1.STR1);就会变成System.out.println(“test”);,所以其实赋值是成功了的,但是因为打印变成常量了所以没变化,我们可以用反射拿出值来验证一下

package org.clown;



import java.lang.reflect.Field;

public class Test1 {
public static String str="ceshi";
public final String STR1="test";
private final StringBuilder name = new StringBuilder("default2");
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;
//修改final的String变量
Field str11 = test1Class.getDeclaredField("STR1");
str11.setAccessible(true);
str11.set(test1,"ceshi2");
System.out.println(test1.STR1);
System.out.println(str11.get(test1));
}
}

image-20241004100943421

可以看到确实是没问题的

那我们想要修改final变量就需要防止String类型变量在编译时被处理为常量,方法就是让其值的初始化经过运算才能得到,我们代码可以改成这样

package org.clown;



import java.lang.reflect.Field;

public class Test1 {
public final String STR1=(null == null ? "default4" : "");
public final String STR2=new StringBuilder("default5").toString();
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;

//修改final的String变量

Field str11 = test1Class.getDeclaredField("STR1");
str11.setAccessible(true);
str11.set(test1,"ceshi5");
System.out.println(test1.STR1);

Field str2 = test1Class.getDeclaredField("STR2");
str2.setAccessible(true);
str2.set(test1,"ceshi6");
System.out.println(test1.STR2);
}
}

image-20241004101525466

这两种方法我们都可以防止其被常量化

修改同时被final和static修饰的变量

此时通过常规反射的写法就会报错,如果我们要修改的话就需要先去除final修饰符才行

package org.clown;



import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test1 {
private final static StringBuilder name1 = new StringBuilder("default7");
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;

//反射同时修改static和final修饰的变量
Field name11 = test1Class.getDeclaredField("name1");
name11.setAccessible(true);
Field modifiers = name11.getClass().getDeclaredField("modifiers");//获取modifiers字段
modifiers.setAccessible(true);
modifiers.setInt(name11, name11.getModifiers() & ~Modifier.FINAL);//去除final修饰符
name11.set(test1,new StringBuilder("ceshi7"));
modifiers.setInt(name11, name11.getModifiers() & ~Modifier.FINAL);//将final修饰符设置回来
System.out.println(test1.name1);
}
}

用modifiers字段去除final修饰符即可修改。

Field 类中的 modifiers 字段。这个字段是一个 int 类型的值,表示该字段的修饰符(如 publicprivatefinal 等),该字段是一个私有字段。

Modifier.FINAL 是一个常量,表示 final 修饰符的位掩码。

高版本修改final和static

其他的反射修改都没有变化,就是修改同时被final和static修饰的变量的方法从Java12开始失效了,我在java17测试是直接没有modifiers这个字段了。这是因为高版本下不能通过getDeclaredFiled获取Field的属性。

这一点可以从fieldFilterMap里面知道

image-20241004105355114

图中被过滤的类都不能直接通过公共反射获取他们的属性了,我们的Field类就在其中。

那就需要改一改方法了,这种方法对java8-java17都是适用的,参考文章:https://blog.csdn.net/wu_weijie/article/details/129251045

我们可以改成用getDeclaredFields0方法来获取,从文章中可以知道他能返回一个Field数组对象,里面就包含了我们的modifiers属性

那我们的修改代码就可以改成下面这样:

package org.clown;



import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test1 {
private final static StringBuilder name1 = new StringBuilder("default7");
public static void main(String[] args) throws Exception {
Test1 test1 = new Test1();
Class test1Class = Test1.class;

//高版本反射同时修改static和final修饰的变量
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
Field modifiers = null;
for (Field each : fields) {
if ("modifiers".equals(each.getName())) {
modifiers = each;
}
}
Field name11 = test1Class.getDeclaredField("name1");
name11.setAccessible(true);
modifiers.setAccessible(true);
modifiers.setInt(name11, name11.getModifiers() & ~Modifier.FINAL);
name11.set(test1,new StringBuilder("ceshi7"));
modifiers.setInt(name11, name11.getModifiers() & ~Modifier.FINAL);
System.out.println(test1.name1);


}
}

我们运行的时候还需要添加模块,因为java12的高版本是模块化的,在vm-options添加修改即可

--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED