版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/barryhappy/article/details/24442953

问题,以及一个解决方式

今天公司的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的改动则“没有生效”。


同步发表在 http://www.barryzhang.com/archives/188

广告一下我的新博客,欢迎訪问哈~:BarryZhang.com  


JAVA反射改动常量,以及其局限的更多相关文章

  1. 基于NACOS和JAVA反射机制动态更新JAVA静态常量非@Value注解

    1.前言 项目中都会使用常量类文件, 这些值如果需要变动需要重新提交代码,或者基于@Value注解实现动态刷新, 如果常量太多也是很麻烦; 那么 能不能有更加简便的实现方式呢? 本文讲述的方式是, 一 ...

  2. Java反射开窍--1

    1.通过案例引出反射并体会反射的好处 案例:美团外卖 --->付款 --->要么用微信支付 要么用支付宝支付 package com.zhaoss.test01; //接口的制定方:美团外 ...

  3. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  4. java基础知识(十一)java反射机制(下)

    1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...

  5. java 反射的应用 以及通过反射 用到的工厂模式

    java反射详解 本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案 ...

  6. JAVA反射实践

    Java反射机制在我的理解当中就是下面几点: 1. 对一个给定的类名(以字符串形式提供)能动态构建一个对象实例 2. 对于任意一个类,都能够知道这个类的所有属性和方法     3. 对于任意一个对象, ...

  7. Java反射机制可以动态修改实例中final修饰的成员变量吗?

    问题:Java反射机制可以动态修改实例中final修饰的成员变量吗? 回答是分两种情况的. 1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了. ...

  8. 【JAVA】JAVA 反射

    在Java反射机制中,需要掌握的知识有:         (1)掌握反射机制的概述.         (2)能够使用Class类并结合java.lang.reflect包取得一个类的完整结构.     ...

  9. Java反射机制的学习

    Java反射机制是Java语言被视为准动态语言的关键性质.Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息,动态地生成此类,并调 ...

随机推荐

  1. iOS学习笔记18-CoreData

    一.CoreData介绍 CoreData是iOS5之后新出来的的一个框架, 是对SQLite进行一层封装升级后的一种数据持久化方式.它提供了对象<-->关系映射的功能,即能够将OC对象转 ...

  2. 【bzoj2229】[Zjoi2011]最小割 分治+网络流最小割

    题目描述 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分 ...

  3. OpenJ_Bailian——4115鸣人和佐助(带状态的A*)

    鸣人和佐助 Time Limit: 1000MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u Submit Status Desc ...

  4. UVA 11297 Census ——二维线段树

    [题目分析] 二维线段树模板题目. 简直就是无比的暴力.时间复杂度为两个log. 标记的更新方式比较奇特,空间复杂度为N^2. 模板题目. [代码] #include <cstdio> # ...

  5. 【2018.10.18】noip模拟赛Day2 地球危机(2018年第九届蓝桥杯C/C++A组省赛 三体攻击)

    题目描述 三体人将对地球发起攻击.为了抵御攻击,地球人派出了 $A × B × C$ 艘战舰,在太 空中排成一个 $A$ 层 $B$ 行 $C$ 列的立方体.其中,第 $i$ 层第 $j$ 行第 $k ...

  6. cf299C Weird Game

    Weird Game Yaroslav, Andrey and Roman can play cubes for hours and hours. But the game is for three, ...

  7. android源码mk文件里的TARGET_OUT指向哪里?

    android源码核心变量大都在build/core/envsetup.mk中建立 在该文件中,可以找到 TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ ...

  8. 矩阵乘法 BZOJ 2738

    矩阵乘法 [问题描述] 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. [输入格式] 第一行两个数N,Q,表示矩阵大小和询问组数:接下来N行N列一共N*N个数,表示这个矩阵: ...

  9. msp430项目编程56

    msp430综合项目---扩展项目六56 1.电路工作原理 2.代码(显示部分) 3.代码(功能实现) 4.项目总结

  10. 用“道”的思想解决费用流问题---取/不取皆是取 (有下界->有上界) / ACdreamoj 1171

    题意: 给一个矩阵,给出约束:i(0<i<n)行至少去ai个数,j行至少取bi个数,要求取的数值之和最小. 开始一见,就直接建了二分图,但是,发现这是有下界无上界最小费用流问题,肿么办.. ...