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



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

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


时间: 2024-08-11 15:04:05

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

Reflect Java反射机制

// 参考:http://blog.csdn.net/stevenhu_223/article/details/9286121 最近发现好多框架底层的实现与Java的reflect和cglib有关,看过原理后找了几篇经典的文档,以供后来复习使用 前言:我们知道,类和类的成员变量及方法都是要求有权限控制的(public.protected.private):而当类中的信息封装为私有时,外部对该类中私有的信息是没有访问权限的,也就是说当该类里的内容信息均受private权限控制时,外部想要获取和处理

JAVA反射实践

Java反射机制在我的理解当中就是下面几点: 1. 对一个给定的类名(以字符串形式提供)能动态构建一个对象实例 2. 对于任意一个类,都能够知道这个类的所有属性和方法 3. 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制,这种反射机制在Java当中给我们带来了极大的方便,被大量地应用于JavaBean中 在Java的反射中我们主要用到以下几个类,下面将逐个说明 java.lang.Class; java.lang.r

Java知识总结:Java反射机制(用实例理解)

概念理解: 反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机制来 实现对自己行为的描述( self-representation )和检测( examination) ,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. Java中的反射是一个强大的工具,他能够创建灵活的代码,这些 代码可以在运行时装配,无需在组件之间进行链接,发射允许在编写和执行时,使程序代码能够接入装载到 JVM 中的类的内部信息 .而不是源代码中选定的类协作的代码.这使发射

从Java到C++——常量(const)的高级应用

看到const关键字,C++程序员首先想到的可能是const常量.这可不是良好的条件反射.如果只知道用const定义常量,那么相当于把火药仅用于制作鞭炮.const更大的魅力是它可以修饰函数的参数.返回值,甚至函数的定义体. const是constant的缩写,"恒定不变"的意思.被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性和高效性.所以很多C++程序设计书籍建议:"Use const whenever you need". 1 用c

java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)

本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package Reflect; /**  * 通过一个对象获得完整的包名和类名  * */ class Demo{     //other codes... } class hello{     pu

【java基础】Java反射机制

一.预先需要掌握的知识(java虚拟机)  1)java虚拟机的方法区:  java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区.方法区的主要作用是存储被装载的类 的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class 中的类型信息,将这些信息存储到方法区中.这些信息主要包括: 这个类型的全限定名 这个类型的直接超类的全限定名 这个类型是类类型还是接口类型

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

1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. 也就是说,java程序可以加载一个运行时才得知名称的class,获悉该类的完整构造(但不包括methods定义),并生成其对象实体,或对其fields设值,或唤起其methods.总之,就是JVM可以在运行时加载.探知.使用编译期完全未知的classes. 2.jdk提供的反射api Java反射相

从Java到C++——常量的定义和应用

常量是一种标识符,它的值在运行期间恒定不变.C语言用 #define来定义常量(称为宏常量).C++ 语言除了 #define外还可以用const来定义常量(称为const常量). 一.为什么需要常量 如果不使用常量,直接在程序中填写数字或字符串,将会有什么麻烦? (1).程序的可读性(可理解性)变差.程序员自己会忘记那些数字或字符串是什么意思,用户则更加不知它们从何处来.表示什么. (2).在程序的很多地方输入同样的数字或字符串,难保不发生书写错误. (3).如果要修改数字或字符串,则会在很多

Java反射及其在Android中的应用学习总结

一. Java反射机制 Reflection 是Java被视为动态(或准动态)语言的一个关键性质.这个机制同意程序在执行时透过Reflection APIs取得不论什么一个已知名称的class的内部信息,包含其modifiers(诸如public, static 等等).superclass(比如Object).实现之interfaces(比如Serializable).也包含fields和methods的全部信息,并可于执行时改变fields内容或调用methods(包含被声明为private