今天公司的JAVA项目碰到一个问题:在生成xls文件的时候,如果数据较多,会出现ArrayIndexOutOfBoundsException。
Google发现是项中所用的jxl包(开源库,用以处理xls文件)的一个BUG。
也找到了一个解决办法:http://www.blogjava.net/reeve/archive/2013/01/11/114564.html——即找到它的源代码,修改其中的一个静态常量,然后重新打包成jar即可。试了一下,这个方法确实可行。
不过后来在公司前辈提醒,可以试一下——
——这样就不用修改jxl库了,只要在我们项目中加几句就OK了,出问题的概率也会小很多。
于是就研究了一下,虽然最后还是发现在这个方法在我们的项目不可行,不过还是很有收获的。
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都不会出现失效的情况。
经过一系列的研究、推测、搜索等过程,终于发现了原因:
参考: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了。
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