Java 反射常量

问:下面程序段中几个 invokeX 方法在运行时哪些可以达到预期效果?哪些不能?为什么?
public class Test {
    private static final Integer KEY_EXIT = 1024;

    private static void invok1() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok1->"+Test.KEY_EXIT);
        Field field = Test.class.getDeclaredField("KEY_EXIT");
        field.setAccessible(true);
        field.set(null, 1000);
        System.out.println("invok1-<"+Test.KEY_EXIT);
    }
    private static void invok2() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok2->"+Test.KEY_EXIT);
        Field field = Test.class.getField("KEY_EXIT");
        field.set(null, 512);
        System.out.println("invok2-<"+Test.KEY_EXIT);
    }
    private static void invok3() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok3->"+Test.KEY_EXIT);
        Field field = Test.class.getDeclaredField("KEY_EXIT");
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, 256);
        System.out.println("invok3-<"+Test.KEY_EXIT);
    }
    public static void main(String[] args) throws Exception {
        invok1();
        invok2();
        invok3();
    }
}

答:上面程序运行结果如下。

//invok1运行结果
invok1->1024
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final java.lang.Integer field Test.KEY_EXIT to java.lang.Integer
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
    at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
    at java.lang.reflect.Field.set(Field.java:764)
    at Test.invok1(Test.java:14)
    at Test.main(Test.java:37)

//invok2运行结果
invok2->1024
Exception in thread "main" java.lang.NoSuchFieldException: KEY_EXIT
    at java.lang.Class.getField(Class.java:1703)
    at Test.invok2(Test.java:20)
    at Test.main(Test.java:38)

//invok3运行结果
invok3->1024
invok3-<256
  • 对于 invok1 方法来说成功获取了 KEY_EXIT 静态常量,但是由于是 final 常量的,不允许直接修改,所以直接调用 set 时发生修改异常。

  • 对于 invok2 方法来说 getFields() 方法只能获得某个类及其父类中的所有的 public 字段,而 getDeclaredFields() 方法却能获得某个类(不包括父类)的所有字段(包括 public、private、proteced 等。同样类似的还有 getConstructors()getDeclaredConstructors()、getMethods()getDeclaredMethods() 方法,所以 invok2 会提示找不到字段而崩溃。

  • 对于 invok3 来说就是标准的反射修改静态常量操作。

问:下面程序段中几个 invokeX 方法的运行结果是什么?
public class Test {
    private static final int KEY_EXIT = 1024;
    private static int KEY_BACK = 1025;
    private static final String KEY_STR = "1001";
    private static void invok1() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok1->"+Test.KEY_EXIT);
        Field field = Test.class.getDeclaredField("KEY_EXIT");
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, 256);
        System.out.println("invok1-<"+Test.KEY_EXIT);
    }
    private static void invok2() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok2->"+Test.KEY_BACK);
        Field field = Test.class.getDeclaredField("KEY_BACK");
        field.setAccessible(true);
        field.set(null, 1000);
        System.out.println("invok2-<"+Test.KEY_BACK);
    }
    private static void invok3() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("invok3->"+Test.KEY_STR);
        Field field = Test.class.getDeclaredField("KEY_STR");
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, "512");
        System.out.println("invok3-<"+Test.KEY_STR);
    }
    public static void main(String[] args) throws Exception {
        invok1();
        invok2();
        invok3();
    }
}

答:上面程序运行结果如下。

//invok1运行结果
invok1->1024
invok1-<1024

//invok2运行结果
invok2->1025
invok2-<1000

//invok3运行结果
invok3->1001
invok3-<1001

有趣的事情发生了。你可能会去 debug 断点调试 invok1 和 invok3,你会发现相关成员属性都得到了正确的反射修改值,但是为什么输出却不是反射修改后的值?

其实原因很简单,当我们定义基本类型的 final 常量或者 String 类型的 final 常量时(只要为 final,不限制有无 static),如果在编译时能确定其确切值则编译器会将其用到的地方用其实际值进行替换,譬如

static final int A = 23; 
println(A); 
if(i > A) {} 

这样的语句会被编译成

static final int A = 23; 
println(23); 
if(i > 23) {}

的形式,所以即便运行时反射成功也没有任何意义,因为相关值已经在编译时被替换为了常量,而对于包装类型则没事。

所以在反射 final 修饰的变量时一定要留意其是否会在编译时被替换的问题,否则不但不会报错还会给自己带来不必要的错觉麻烦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 一:java概述: 1,JDK:Java Development Kit,java的开发和运行环境,java的开发...
    慕容小伟阅读 1,788评论 0 10
  • 今天看了一位小战友的留言,脑袋里冒出的就是这句话,以铜为镜,可以正衣冠;以人为镜可以知得失。 从时间上看自...
    林多多1995阅读 646评论 0 0
  • 大多数汽车购物者要么以现金支付现金,要么通过贷款为他们的购买提供资金,但是你正在考虑加入那些愿意放弃新车拥有权的无...
    Talk车君阅读 325评论 0 0
  • 今天中午,我们观看了《奇迹男孩》这部电影,主人公奥吉因为长的非常奇怪也很难看,而备受人们的嘲笑。但是他不是去帮助别...
    梦_20d9阅读 187评论 0 0