转载自 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是私有静态常量
class Bean{  private static final Integer INT_VALUE = 100;
}  
修改常量的核心代码:
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之后,

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都不会出现失效的情况。

原因

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

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

参考:Modifying final fields in Java

即对于常量 public static final int maxFormatRecordsIndex = 100 ,代码

if( index > maxFormatRecordsIndex   ){  index  =  maxFormatRecordsIndex ;
}  

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

if( index > 100){  index = 100;
} 

所以在INT_VALUE是int类型的时候

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

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

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

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


附一下我测试时候的DEMO吧

代码

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

以上代码输出的结果是:

JAVA反射修改常量,以及其局限相关推荐

  1. java反射 修改静态方法的值 setAccessible

    转载自 java反射 修改静态方法的值 setAccessible 这几天闲来无事.在网上看了一个题目,相信大家都知道这个题目 static void change(String str){str=& ...

  2. Java 反射修改类的常量值、静态变量值、属性值

    前言 有的时候,我们需要修改一个变量的值,但变量也许存在于 Jar 包中或其他位置,导致我们不能从代码层面进行修改,于是我们就用到了下面的场景,通过反射来进行修改变量的值. 定义一个实体类 class ...

  3. java 反射修改final变量_Java中final的属性值怎么利用反射机制进行修改

    Java中final的属性值怎么利用反射机制进行修改 发布时间:2020-12-02 17:31:07 来源:亿速云 阅读:96 作者:Leah 今天就跟大家聊聊有关Java中final的属性值怎么利 ...

  4. java 反射 修改字段_Java反射机制-修改字段值

    需要用到的类: Field 需要用到的方法: getDeclaredField(); getField(); setAccessible(); 需要用到的字段: public int a=5; pub ...

  5. Java反射修改返回值_java反射调用时如果返回值是整型数组怎么处理?

    想通过java的反射功能将javaBean转换成map: public static Map toMap(Object javaBean) { Map result = new HashMap< ...

  6. java反射设置属性值_Java反射如何有效的修改final属性值详解

    前言 以前写过一篇 Java 反射修改 final 属性值,本文将在这里重新温习一下Java反射如何有效的修改final属性值,下面话不多说了,来一起看看详细的介绍: 假设有个类 class Pers ...

  7. java 反射 int_Java 反射由浅入深 | 进阶必备

    原标题:Java 反射由浅入深 | 进阶必备 一.Java 反射机制 参考了许多博文,总结了以下个人观点,若有不妥还望指正: Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性 ...

  8. Java:反射机制学习

    文章目录 前言 一.Java反射机制具体是什么 为什么Java要有反射机制,为什么像C++之类的语言不需要有反射机制 反射机制宏观上能让我们做些什么 二.通过反射获取运行时标识Class类 1.Cla ...

  9. 不藏了,这些Java反射用法总结都告诉你们

    摘要:Java反射是一种非常强大的机制,它可以在同一个系统中去检测内部的类的字段.方法和构造函数.它非常多的Java框架中,都大量应用了反射技术,如Hibernate和Spring.可以说,反射机制的 ...

最新文章

  1. 多线程模式(2):Guarded Suspension模式
  2. pytorch lstm 写诗文的魔改,测试,猜想
  3. 蓝牙 GATT 协议
  4. Could not install packages due to an Environment Error: [Errno 13] Permission denied 解决方案
  5. 依图又斩获10冠!AI芯片拿下创新技术大奖,朱珑再谈智能密度
  6. 迷茫与飞跃:9月开始,明确了研究方向,功力提升明显,成绩比较显著
  7. AttributeError: module ‘cv2‘ has no attribute ‘SIFT‘
  8. JS实现的展开隐藏效果
  9. 重学java基础第十七课:java开发环境搭建
  10. windows下安装、卸载mysql服务
  11. C++ 刷算法题笔记
  12. ROS通过串口,读写STM32和HC-SR04超声波测距信息
  13. 一文把三个经典求和问题吃的透透滴。
  14. 更改网页alert弹出框样式
  15. AutoPatch java_《Oracle Applications DBA 基础》5- AD 工具 and patching
  16. java 杨辉三角_Java语言杨辉三角
  17. entity framework 新手入门篇(2)-entity framework基本的增删改查
  18. 【转载】日本社会为啥没有“王思聪”
  19. swal()弹出删除确认框
  20. python画一片树叶的故事_《Python数据分析与机器学习实战-唐宇迪》读书笔记第7章--决策树...

热门文章

  1. [MySQL基础]MySQL语法规范介绍
  2. 《C++ Primer》7.5.2节练习
  3. 网络原理往期考试题+部分详解+最终版
  4. word List 46
  5. mysql_result函数用不了_mysql_result()函数怎么在PHP中使用
  6. pta数据结构 彩虹瓶(c++实现)(详解)
  7. mmdnn TensorFlow is outdated
  8. HDU - 7054 Yiwen with Formula 分治拆位FFT + dp + 费马小定理降幂
  9. Knowledge Test about Match
  10. Rinne Loves Xor