问题:今天项目有一个需求,需要new一个HashMap,将它赋值给一个修饰符为static+final的Map。
思路:不能停服,那就只能动态修改了,那必然用到反射。反射的一些基础知识请自行学习
代码:
1 /**
2 * 修改静态final字段的值
3 * @author chenzl
4 * 2015-09-22
5 */
6 public classSetFinalValue {
7
8 public static final Map openMap = new HashMap();
9
10 public static void main(String[] args) throwsException {
11 Field target = SetFinalValue.class.getField(“openMap”);
12
13 int modify =target.getModifiers();
14 SetFinalValue.checkModifier(modify);
15
16 Map openMap2 = new HashMap();
17 openMap2.put(2, “abcde”);
18 try{
19 //取消 Java 语言访问检查,详细查看 API(这里可以不写)
20 target.setAccessible(true);
21
22 //获得修饰符Field对象,通过这个对象可以对另外一个Field对象的操作符进行修改,源码见图-1
23 Field modifiersField = Field.class.getDeclaredField(“modifiers”);
24 modifiersField.setAccessible(true);
25
26 //关于Modefier常量的定义见图-2
27 modify = target.getModifiers() & ~Modifier.FINAL;
28 System.out.println(“处理后的 modify : ” +modify);
29
30 //更改目标对象的修饰符
31 modifiersField.setInt(target, modify);
32 modify =target.getModifiers();
33
34 System.out.println(“#####更改修饰符后的结果######”);
35 SetFinalValue.checkModifier(modify);
36
37 //更改静态常量
38 target.set(null, openMap2);
39
40 System.out.println(openMap.get(2));
41 } catch(Exception e) {
42 e.printStackTrace();
43 }
44
45 /**重复设置一次*/
46 Map openMap3 = new HashMap();
47 openMap3.put(3, “中文输入”);
48 try{
49 target = SetFinalValue.class.getField(“openMap”);
50 target.setAccessible(true);
51 System.out.println(“#####重复一次检验一次重新get后值会不会改变######”);
52 checkModifier(target.getModifiers());
53
54 } catch(Exception e) {
55 e.printStackTrace();
56 }
57
58 }
59
60 /**
61 * 检查所有的修饰符,是否是 public static final
62 * @param modify
63 */
64 public static void checkModifier(intmodify){
65 System.out.println(“当前的 modify : ” +modify);
66 //源码见图-3
67 System.out.println(” public : ” +Modifier.isPublic(modify));
68 System.out.println(” static : ” +Modifier.isStatic(modify));
69 System.out.println(” final : ” +Modifier.isFinal(modify));
70 }
71 }
程序结果:
总结:先拿到成员变量的Field对象,从Field对象中获得所有修饰符,修改它的修饰符,然后设置对象的值。
图-1:
图-2:
图-3:
其他:
1、测试基本数据类型 int
2、测试Integer类型:
3、String类型
3.1 String类型的其他方式
关于String类型出现的特殊情况,我暂时也不知道原因,猜测是与String类型的常量池有关,有待我以后去证实。
3.2 当我用一个新的常量去替换时成功了。
那么我暂时理解成 open变量指向常量池的一个内存地址,在编译器就确定了,不能动态指向常量池外的内存地址,只能重新指向常量池内的另外一个内存地址。
2017-7-29 修改:
jdk版本:1.8.0.31
文章的最后出现一个错误,3.2中的open是一个新new的对象,不管是直接传入“开始”还是传入str都可以修改它。如果open是通过对象池创建的,则不会改变。