问题,以及一个解决方案
今天公司的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是私有静态常量
[java]?view plaincopyprint?
?
class="dp-j">
- class?Bean{??
- ????private?static?final?Integer?INT_VALUE?=?100;??
- }??
修改常量的核心代码:
[java]?view plaincopyprint?
?
- System.out.println(Bean.INT_VALUE);??
- ??
- Field?field?=?Bean.class.getField("INT_VALUE");??
- ??
- field.setAccessible(true);??
- ??
- Field?modifiersField?=?Field.class.getDeclaredField("modifiers");??
- modifiersField.setAccessible(true);??
- modifiersField.setInt(field,?field.getModifiers()?&?~Modifier.FINAL);??
- ??
- 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;??
- }??
在其他代码都不变的情况下,代码输出的结果竟然变成了诡异的:
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 ,代码
[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的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。
这大概是JAVA反射的一个局限吧——修改基本类型的常量时,不是太可靠。
附一下我测试时候的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的修改则“没有生效”。
?
?
转自:http://blog.csdn.net/barryhappy/article/details/24442953/