`

【转】java 反射的局限性

阅读更多

问题,以及一个解决方案

今天公司的JAVA项目碰到一个问题:在生成xls文件的时候,如果数据较多,会出现ArrayIndexOutOfBoundsException。
Google发现是项中所用的jxl包(开源库,用以处理xls文件)的一个BUG。
也找到了一个解决办法:http://www.blogjava.net/reeve/archive/2013/01/11/114564.html——即找到它的源代码,修改其中的一个静态常量,然后重新打包成jar即可。试了一下,这个方法确实可行。


另一个解决方案——反射

不过后来在公司前辈提醒,可以试一下——

利用java的反射,在运行时将需要修改的常量强制更改成我们所需要的值

——这样就不用修改jxl库了,只要在我们项目中加几句就OK了,出问题的概率也会小很多。
于是就研究了一下,虽然最后还是发现在这个方法在我们的项目不可行,不过还是很有收获的。

首先,利用反射修改私有静态常量的方法

对如下Bean类,其中的INT_VALUE是私有静态常量
  1. class Bean{  
  2.     private static final Integer INT_VALUE = 100;  
  3. }  
修改常量的核心代码:
  1. System.out.println(Bean.INT_VALUE);  
  2. //获取Bean类的INT_VALUE字段  
  3. Field field = Bean.class.getField("INT_VALUE");  
  4. //将字段的访问权限设为true:即去除private修饰符的影响  
  5. field.setAccessible(true);  
  6. /*去除final修饰符的影响,将字段设为可修改的*/  
  7. Field modifiersField = Field.class.getDeclaredField("modifiers");  
  8. modifiersField.setAccessible(true);  
  9. modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  10. //把字段值设为200  
  11. field.set(null200);  
  12. System.out.println(Bean.INT_VALUE);  

以上代码输出的结果是:

100
200

说明用反射私有静态常量成功了。


方案的局限

注意到上述代码的中的静态常量类型是Integer——但是我们项目中实际需要修改的字段类型并不是包装类型Integer,而是java的基本类型int。
当把常量的类型改成int之后,

  1. class Bean{  
  2.     private static final int INT_VALUE = 100;//把类型由Integer改成了int  
  3. }  

在其他代码都不变的情况下,代码输出的结果竟然变成了诡异的:

100
100

而且在调试的过程中发现,在第二次输出的时候,内存中的Bean.INT_VALUE是已经变成了200,但是System.out.println(Bean.INT_VALUE)输出的结果却依然时诡异的100?!

——反射失效了吗?

又试了其他几种类型,发现这种貌似失效的情会发生在int、long、boolean以及String这些基本类型上,而如果把类型改成Integer、Long、Boolean这种包装类型,或者其他诸如Date、Object都不会出现失效的情况。

原因

经过一系列的研究、推测、搜索等过程,终于发现了原因:

对于基本类型的静态常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。

参考:Modifying final fields in Java
即对于常量 public static final int maxFormatRecordsIndex = 100 ,代码

  1. if( index > maxFormatRecordsIndex   ){  
  2.     index  =  maxFormatRecordsIndex ;  
  3. }       

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

  1. if( index > 100){  
  2.     index = 100;  
  3. }  

所以在INT_VALUE是int类型的时候

  1. System.out.println(Bean.INT_VALUE);  
  2. //编译时会被优化成下面这样:  
  3. System.out.println(100);  

所以,自然,无论怎么修改Boolean.INT_VALUE,System.out.println(Bean.INT_VALUE)都还是会依然固执地输出100了。

——这本身是JVM的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。

这大概是JAVA反射的一个局限吧——修改基本类型的常量时,不是太可靠。


附一下我测试时候的DEMO吧

代码

  1. import java.lang.reflect.Field;  
  2. import java.lang.reflect.Modifier;  
  3. import java.util.Date;  
  4.   
  5. public class ForClass {  
  6.     static void setFinalStatic(Field field, Object newValue) throws Exception {  
  7.         field.setAccessible(true);  
  8.         Field modifiersField = Field.class.getDeclaredField("modifiers");  
  9.         modifiersField.setAccessible(true);  
  10.         modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  11.         field.set(null, newValue);  
  12.     }  
  13.   
  14.     public static void main(String args[]) throws Exception {  
  15.   
  16.         System.out.println(Bean.INT_VALUE);  
  17.         setFinalStatic(Bean.class.getField("INT_VALUE"), 200);  
  18.         System.out.println(Bean.INT_VALUE);  
  19.   
  20.         System.out.println("------------------");  
  21.         System.out.println(Bean.STRING_VALUE);  
  22.         setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");  
  23.         System.out.println(Bean.STRING_VALUE);  
  24.           
  25.         System.out.println("------------------");  
  26.         System.out.println(Bean.BOOLEAN_VALUE);  
  27.         setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);  
  28.         System.out.println(Bean.BOOLEAN_VALUE);  
  29.   
  30.         System.out.println("------------------");  
  31.         System.out.println(Bean.OBJECT_VALUE);  
  32.         setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());  
  33.         System.out.println(Bean.OBJECT_VALUE);  
  34.   
  35.     }  
  36. }  
  37.   
  38. class Bean {  
  39.     public static final int INT_VALUE = 100;  
  40.     public static final Boolean BOOLEAN_VALUE = false;  
  41.     public static final String STRING_VALUE = "String_1";  
  42.     public static final Object OBJECT_VALUE = "234";  
  43. }  

代码输出

100
100
------------------
String_1
String_1
------------------
false
true
------------------
234
Fri Apr 25 00:55:05 CST 2014

 

说明

——其中的Boolean跟Object类型常量被正确修改了,而基本类型int和String的修改则“没有生效”。

 

 

转自:http://blog.csdn.net/barryhappy/article/details/24442953/

分享到:
评论

相关推荐

    Java后端+Java后端中级面试题

    请解释Java中的反射机制,以及它的用途和局限性。 什么是Java的序列化和反序列化?为什么它们在分布式系统中很重要? 解释什么是Java的设计模式,并列举几个常用的设计模式及其应用场景。 这些题目涵盖了Java开发中...

    Java大数据开发+Java大厂面试题

    请解释Java中的反射机制,以及它的用途和局限性。 什么是Java的序列化和反序列化?为什么它们在分布式系统中很重要? 解释什么是Java的设计模式,并列举几个常用的设计模式及其应用场景。 这些题目涵盖了Java开发中...

    Java面试题+Java后端中级面试题

    请解释Java中的反射机制,以及它的用途和局限性。 什么是Java的序列化和反序列化?为什么它们在分布式系统中很重要? 解释什么是Java的设计模式,并列举几个常用的设计模式及其应用场景。 这些题目涵盖了Java开发中...

    java 编程入门思考

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 ...

    Java初学者入门教学

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 ...

    java联想(中文)

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 ...

    Java2核心技术.part5

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 ...

    JAVA_Thinking in Java

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 ...

    Thinking in Java简体中文(全)

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 ...

    Thinking in Java 中文第四版+习题答案

    11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 12.2.4...

    java学习笔记 初学者必读

    17.1.7. 泛型的一些局限型 17-65 17.2. 增强的for循环 17-66 17.3. 自动装箱和自动拆箱 17-69 17.3.1. 在基本数据类型和封装类之间的自动转换 17-69 17.4. 类型安全的枚举 17-70 17.5. 静态引入 17-71 17.6. C风格的...

    \java超强笔记(超级经典)

    泛型的局限性: 不能实例化泛型 T t = new T(); //error 数组不可用泛型限定 List[] list = new List[10]; //错误 E[] a = new E[10]; //错误 类的静态变量不能声明为类的泛型类型 ...

    corejava培训文档

    17.1.7. 泛型的一些局限型 17.2. 增强的for循环 17.3. 自动装箱和自动拆箱 17.3.1. 在基本数据类型和封装类之间的自动转换 17.4. 类型安全的枚举 17.5. 静态引入 17.6. C风格的格式化输出 17.7. Building ...

    Java2核心技术.part3

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 ...

    Java2核心技术.part1

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 13.8.1...

    Java2核心技术.part6

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 ...

    Java2核心技术.part4

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 ...

    Java2核心技术.part2

    13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 ...

Global site tag (gtag.js) - Google Analytics