Java 常量改变
2015-09-05 22:32
387 查看
<pre name="code" class="java">import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class Test1 { static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } public static void main(String args[]) throws Exception { System.out.println(Bean.OBJECT_VALUE); Srr srr= new Srr(); srr.setUrl("www.baidu.com"); setFinalStatic(Bean.class.getField("OBJECT_VALUE"), srr); System.out.println(((Srr)Bean.OBJECT_VALUE).getUrl()); } } class Bean { public static final Object OBJECT_VALUE = "www.srr.com"; } class Srr { private String url="srr"; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
对如下Bean类,其中的INT_VALUE是私有静态常量
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
class Bean{
private static final Integer INT_VALUE = 100;
}
修改常量的核心代码:
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
System.out.println(Bean.INT_VALUE);
//获取Bean类的INT_VALUE字段
Field field = Bean.class.getField("INT_VALUE");
//将字段的访问权限设为true:即去除private修饰符的影响
field.setAccessible(true);
/*去除final修饰符的影响,将字段设为可修改的*/
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
//把字段值设为200
field.set(null, 200);
System.out.println(Bean.INT_VALUE);
以上代码输出的结果是:
100
200
说明用反射私有静态常量成功了。
方案的局限
注意到上述代码的中的静态常量类型是Integer——但是我们项目中实际需要修改的字段类型并不是包装类型Integer,而是java的基本类型int。当把常量的类型改成int之后,
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
class Bean{
private static final int INT_VALUE = 100;//把类型由Integer改成了int
}
在其他代码都不变的情况下,代码输出的结果竟然变成了诡异的:
100
100
而且在调试的过程中发现,在第二次输出的时候,内存中的Bean.INT_VALUE是已经变成了200,但是System.out.println(Bean.INT_VALUE)输出的结果却依然时诡异的100?!
——反射失效了吗?
又试了其他几种类型,发现这种貌似失效的情会发生在int、long、boolean以及String这些基本类型上,而如果把类型改成Integer、Long、Boolean这种包装类型,或者其他诸如Date、Object都不会出现失效的情况。原因
经过一系列的研究、推测、搜索等过程,终于发现了原因:
对于基本类型的静态常量,J***A在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。
参考:Modifying final fields in Java即对于常量 public static final int maxFormatRecordsIndex = 100 ,代码
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
if( index > maxFormatRecordsIndex ){
index = maxFormatRecordsIndex ;
}
这段代码在编译的时候已经被java自动优化成这样的:
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
if( index > 100){
index = 100;
}
所以在INT_VALUE是int类型的时候
[java] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
System.out.println(Bean.INT_VALUE);
//编译时会被优化成下面这样:
System.out.println(100);
所以,自然,无论怎么修改Boolean.INT_VALUE,System.out.println(Bean.INT_VALUE)都还是会依然固执地输出100了。
——这本身是JVM的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。
这大概是J***A反射的一个局限吧——修改基本类型的常量时,不是太可靠。
附一下我测试时候的DEMO吧
代码
[java] viewplaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
public class ForClass {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
System.out.println(Bean.INT_VALUE);
setFinalStatic(Bean.class.getField("INT_VALUE"), 200);
System.out.println(Bean.INT_VALUE);
System.out.println("------------------");
System.out.println(Bean.STRING_VALUE);
setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");
System.out.println(Bean.STRING_VALUE);
System.out.println("------------------");
System.out.println(Bean.BOOLEAN_VALUE);
setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);
System.out.println(Bean.BOOLEAN_VALUE);
System.out.println("------------------");
System.out.println(Bean.OBJECT_VALUE);
setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());
System.out.println(Bean.OBJECT_VALUE);
}
}
class Bean {
public static final int INT_VALUE = 100;
public static final Boolean BOOLEAN_VALUE = false;
public static final String STRING_VALUE = "String_1";
public static final Object OBJECT_VALUE = "234";
}
代码输出
100 100 ------------------ String_1 String_1 ------------------ false true ------------------ 234 Fri Apr 25 00:55:05 CST 2014
说明
——其中的Boolean跟Object类型常量被正确修改了,而基本类型int和String的修改则“没有生效”。相关文章推荐
- 修改Eclipse创建项目的默认charset 为utf-8
- Java Io 流(输入输出流)
- myeclipse 破解 生成注册码的代码
- 修改Android签名证书keystore作为eclipse默认debug签名证书
- Java学习路线图
- 解决为什么每次打开Eclipse新的workspace需要更新nexus-maven-repository-index问题
- 介绍一下Java的事务处理
- struts2自定义声明校验器
- java内存设置
- SpringMVC框架
- Java文件的MD5校验和CRC校验
- [Java基础]Java新手通病的知识总结
- java 用new语句创建(实例化)对象的三种方法的性能对比
- JAVA实现DES加密
- java复习 --集合类
- JAVA实现AES加密
- java笔记—初识面向对象01
- Java中的instanceof关键字
- 深入理解java异常处理机制
- java中关于锁的关键字