您的位置:首页 > 编程语言 > Java开发

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?





class Bean{

private static final Integer INT_VALUE = 100;

}

修改常量的核心代码:

[java] view
plaincopyprint?





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?





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?





if( index > maxFormatRecordsIndex ){

index = maxFormatRecordsIndex ;

}

这段代码在编译的时候已经被java自动优化成这样的:

[java] view
plaincopyprint?





if( index > 100){

index = 100;

}

所以在INT_VALUE是int类型的时候

[java] view
plaincopyprint?





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] view
plaincopyprint?





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的修改则“没有生效”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: