问题,以及一个解决方案
今天公司的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://blog.csdn.net/barryhappy/article/details/24442953/
相关推荐
虽然Java反射机制提供了很多强大的功能,但它也有一些局限性和需要注意的地方: - **性能开销**:使用反射通常会带来一定的性能开销,因为它涉及到额外的查找和类型解析步骤。 - **安全性和稳定性**:反射可以访问...
Java反射机制允许程序在运行时获取类的信息,这使得Java具有了一定程度上的动态性。具体来说,Java反射机制提供了以下功能: 1. **获取类的信息**:可以在运行时获取类的修饰符、父类、实现接口等信息。 2. **操作...
#### 五、反射机制的局限性 虽然Java反射机制带来了极大的灵活性,但它也有一些潜在的问题需要注意: 1. **性能开销**:反射操作通常比直接调用慢得多,因为它涉及到类的解析和方法的查找。 2. **安全性问题**:...
#### 四、Java反射机制的优势与局限性 1. **优势** - 动态性:可以在运行时决定创建哪个类的对象,并调用其方法。 - 灵活性:能够根据不同的输入参数创建不同的对象或执行不同的操作。 2. **局限性** - 性能...
### Java反射机制应用详解 #### 一、Java反射机制简介 Java反射机制是Java语言提供的一种能在运行时分析类信息并动态操作对象的...然而,反射也有其局限性,比如性能问题、安全问题等,因此在使用时也需要权衡利弊。
### JAVA反射机制详解 #### 一、JAVA反射机制概述 **反射**是在1982年由Smith首次提出的概念,指的是程序有能力访问、检测并修改其自身的状态或行为。这一概念一经提出,便迅速引起了计算机科学领域的关注,并在多...
8. **局限性和注意事项** - 反射操作相对较慢,因为它涉及到运行时的查找和解析。 - 反射可能会破坏封装性,直接修改私有属性或方法可能导致意外的结果。 - 反射操作可能引发`IllegalAccessException`、`...
### Java反射机制详解 ...然而,反射也有其局限性和潜在的风险,比如性能问题、安全性问题等,因此在使用反射时需要谨慎考虑。通过本文的学习,希望能够帮助大家更好地理解和运用反射这一重要的Java特性。
### Java的反射机制详解 #### 一、Java反射机制概述 ...然而,反射也有其局限性和潜在的问题,比如性能问题和安全性问题。因此,在使用反射时应当谨慎考虑其适用场景,并合理利用以发挥其最大价值。
Java反射是Java编程语言中的一个强大特性,...总的来说,Java反射为开发者提供了深入洞察和操控程序运行时行为的能力,极大地扩展了Java编程的可能性。然而,使用时需谨慎,避免过度使用反射导致的性能损失和安全风险。
### Java反射机制概述 Java反射机制是指Java程序在运行时,可以动态地获取一个类的信息以及调用其方法的机制。这是Java语言的一个重要特性,允许程序在运行时检查和修改类的行为。 #### 反射机制的基本操作 在...
5. **IKVM的局限性和挑战**: - 虽然IKVM提供了跨平台的可能性,但它并不支持所有的Java特性,例如JNI(Java Native Interface)和某些复杂的反射操作。 - 性能可能受到一定影响,因为Java字节码需要经过转换和...
### Java反射机制详解 #### 一、引言 Java反射机制是Java编程语言的一个核心特性,它允许程序在运行时动态地...然而,反射机制也有其局限性和潜在的风险,例如性能开销较大、破坏封装性等,因此在使用时需谨慎考虑。
Java 和 C# 都是流行的面向对象编程语言,它们在语法和某些概念上有许多相似之处,但也存在一些差异...如果使用像Demo_Java_to_CSharp_Converter这样的工具,要确保了解其转换规则和局限性,以便在必要时进行手动调整。
#### 四、反射机制的局限性及注意事项 虽然反射提供了强大的功能,但也存在一些局限性和潜在的问题: - **性能开销**:反射涉及到大量的字节码操作,这会增加程序的运行时间。 - **安全性问题**:反射可以访问私有...
#### 四、Java反射技术的优势与局限性 1. **优势**: - 提高代码的灵活性和扩展性。 - 可以实现一些常规方式难以实现的功能。 - 方便进行单元测试。 2. **局限性**: - 性能开销大,反射操作比直接的Java方法...
### Java反射机制详解 #### 一、反射的概念与意义 反射是Java编程语言的一个核心特性,它赋予程序自我检查及动态操作的能力。这种能力最早由Smith在1982年提出,并迅速引起了计算机科学界的广泛关注。反射的概念...
但是这种方式存在一定的局限性。比如,如果需要根据不同的情况选择使用不同的数据结构(如`HashMap`, `LinkedHashMap`, 或者`WeakHashMap`),则每次都需要修改代码、编译、打包再部署,这个过程不仅繁琐而且效率...
### JAVA反射机制详解 #### 一、引言 本文通过几个具体的Java反射示例来深入浅出地介绍...当然,反射也有其局限性和潜在的问题,如性能开销和安全性问题等。因此,在实际项目中应当谨慎使用反射,确保其合理运用。