`
yes1983
  • 浏览: 38565 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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的修改则“没有生效”。

分享到:
评论

相关推荐

    Java_base.rar_java ppt_java基础 ppt

    Java是一种广泛使用的高级编程语言,由Sun Microsystems(后被Oracle公司收购)开发,以其“一次编写,到处运行”的特性而闻名。本资料包是关于Java基础的教程,以PPT形式呈现,适合初学者和想要巩固Java基础知识的...

    Java中枚举的应用.docx

    枚举在Java中被引入,主要是为了克服常量集(通常用public static final定义)的一些局限性,比如防止非法值的创建,增强类型安全性,并支持面向对象编程特性。 1. **单独声明的枚举** 当枚举单独声明时,它类似于...

    JAVA编程百例.rar

    8. **反射机制**:Java反射API允许在运行时动态地获取类的信息并操作类的对象,这对于理解和编写工具类、插件系统等非常有用。 9. **泛型**:泛型是Java 5引入的新特性,用于提供类型安全,减少类型转换,提高代码...

    XJad最好用的java反编译工具

    XJad的独特之处在于它的灵活性和可配置性,用户可以通过修改其脚本来定制反编译过程,这在处理特定类型的字节码时非常有用。 XJad的工作原理是解析.class文件中的字节码指令,然后将其转换为类似于Java源代码的表示...

    head first java

    14. **反射**:Java反射机制允许程序在运行时检查和操作类、接口、方法和字段,增强了程序的动态性。 15. **泛型**:泛型引入了类型参数,提高了代码的类型安全性和可读性,避免了类型转换的麻烦。 通过阅读《Head...

    Java2核心技术.part5

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝...

    java基础教程ppt

    通过学习这些基础知识,你可以编写出功能丰富的应用程序,并逐渐深入到更高级的Java特性,如反射、注解、Lambda表达式等。Java的生态系统庞大,有大量的开源框架和工具可供使用,如Spring Boot、Hibernate、Maven等...

    Java2核心技术.part3

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝...

    Java2核心技术.part1

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝性 1. 2.6...

    Java2核心技术.part6

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝...

    Java2核心技术.part4

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝...

    Java2核心技术.part2

    Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝...

    Java 执行本地脚本携带多参数

    标题 "Java 执行本地脚本携带多参数" 涉及到的是在Java程序中调用本地操作系统脚本,并向这些脚本传递多个参数的技术。这通常涉及到Java的`Runtime`类、`ProcessBuilder`类或者`ExecutorService`来执行系统命令。...

    jdk5.0源代码下载

    - **枚举**:新增的枚举类型使得常量的表示更加规范,支持方法和继承,解决了常量类的局限性。 - **自动装箱与拆箱**:自动装箱允许基本类型与对应的包装类之间无缝转换,简化了编程。 - **增强的for循环**...

    Java开发技术大全 电子版

    Java开发技术大全 电子版 第1篇Java基础知识入门. 第1章Java的开发运行环境2 1.1Java的运行环境与虚拟机2 1.2Java的开发环境4 1.2.1JDK的安装4 1.2.2如何设置系统环境变量6 1.2.3编译命令的使用8 1.2.4解释...

    编码忍者Java

    10. **IO和NIO**:了解传统的IO模型及其局限性,以及Java的非阻塞IO(New IO)框架,提升程序性能。 11. **网络编程**:使用Socket编程实现客户端和服务器之间的通信。 12. **反射和注解**:理解运行时动态类型...

    教学实施大纲(javaWEB方向)

    - **Java中的运算符**:Java支持算术运算符(如+、-)、关系运算符(如>、<)、逻辑运算符(如&&、||)以及位运算符(如&、|)等。 **3. 流程控制** - **IF控制语句**:用于根据条件执行不同的代码块。 - **Switch...

    Kotlin学习文档

    它在2011年由JetBrains团队发布,旨在解决Java的一些局限性,同时保持与Java的高度兼容性。 - **表现力**:Kotlin引入了许多创新的语言特性,比如类型安全的构建器和委托属性,这些特性有助于开发者创建强大而易于...

    proguard-7.0.0.tar.gz

    - ProGuard也有其局限性,对于一些复杂的代码结构和依赖关系,可能无法完全优化。 总之,ProGuard 7.0.0是Android开发者的重要工具,它提供了一种有效的方式来保护和优化代码,提高应用的性能和安全性。熟练掌握其...

Global site tag (gtag.js) - Google Analytics