低版本修改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; Field str1 = test1Class.getDeclaredField("str"); str1.setAccessible(true); str1.set(test1,"ceshi1"); System.out.println(Test1.str); } }
|
修改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; Field name1 = test1Class.getDeclaredField("name"); name1.setAccessible(true); name1.set(test1,new StringBuilder("ceshi3")); System.out.println(test1.name); } }
|
看到是可以修改成功的
但是如果我们修改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; Field str11 = test1Class.getDeclaredField("STR1"); str11.setAccessible(true); str11.set(test1,"ceshi2"); System.out.println(test1.STR1); System.out.println(str11.get(test1)); } }
|
可以看到确实是没问题的
那我们想要修改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;
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); } }
|
这两种方法我们都可以防止其被常量化
修改同时被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;
Field name11 = test1Class.getDeclaredField("name1"); name11.setAccessible(true); Field modifiers = name11.getClass().getDeclaredField("modifiers"); 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); } }
|
用modifiers字段去除final修饰符即可修改。
Field
类中的 modifiers
字段。这个字段是一个 int
类型的值,表示该字段的修饰符(如 public
、private
、final
等),该字段是一个私有字段。
Modifier.FINAL
是一个常量,表示 final
修饰符的位掩码。
高版本修改final和static
其他的反射修改都没有变化,就是修改同时被final和static修饰的变量的方法从Java12开始失效了,我在java17测试是直接没有modifiers这个字段了。这是因为高版本下不能通过getDeclaredFiled获取Field的属性。
这一点可以从fieldFilterMap里面知道
图中被过滤的类都不能直接通过公共反射获取他们的属性了,我们的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;
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
|