大家都知道使用java反射可以在运行时动态改变对象的行为,甚至是private final的成员变量,但并不是所有情况下,都可以修改成员变量。今天就举几个小例子说明。 

首先看下对基本类型的修改:

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {private final int age=18; public int getAge(){
return age;
}
}

测试代码:

import java.lang.reflect.Field;

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionTest {
public static void main(String[] args){ try {
Class reflectionUsage = Class.forName("practise.practise.ReflectionUsage");
ReflectionUsage o = (ReflectionUsage)reflectionUsage.newInstance();
Field age = reflectionUsage.getDeclaredField("age");
age.setAccessible(true);
age.set(o,68);
age.setAccessible(false);
System.out.println(o.getAge());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}catch (IllegalAccessException | InstantiationException e){ }
}
}

运行结果:18

此时无法修改成员变量age.

如果将初始化age放到构造函数中会如何呢:

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {private final int age; public ReflectionUsage(){
this.age=18;
} public int getAge(){
return age;
}
}

再执行RefectionTest会发生什么呢?会发现结果变成了68。

为什么会发生这种情形呢?看一下这两个情况下生成的class文件有什么不同:

直接初始化:

public class ReflectionUsage {
private final int age = 18; public ReflectionUsage() {
} public int getAge() {
return 18;
}
}

构造函数中初始化:

public class ReflectionUsage {
private final int age = 18; public ReflectionUsage() {
} public int getAge() {
return this.age;
}
}

可以看出如果直接初始化时,经过编译后getAge方法直接返回常量值,而在构造函数中初始化时,返回的是this.age变量的值。

再看下对String类型的修改:

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {
private final String reflectionString="reflectionString";
public String getReflectionString(){
return reflectionString;
}
}

测试代码:

import java.lang.reflect.Field;

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionTest {
public static void main(String[] args){
try {
Class reflectionUsage = Class.forName("practise.practise.ReflectionUsage");
ReflectionUsage o = (ReflectionUsage)reflectionUsage.newInstance();
Field reflectionString = reflectionUsage.getDeclaredField("reflectionString");
reflectionString.setAccessible(true);
reflectionString.set(o,"newReflectionString");
reflectionString.setAccessible(false);
System.out.println(o.getReflectionString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}catch (IllegalAccessException | InstantiationException e){ } }
}

运行结果:reflectionString

如果将初始化reflectionString放到构造函数中又如何呢?

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {
private final String reflectionString;
public ReflectionUsage(){
this.reflectionString="reflectionString";
}
public String getReflectionString(){
return reflectionString;
}
}

运行结果:newReflectionString

为什么运行结果不同呢,来看下class文件:

直接初始化:

public class ReflectionUsage {
private final String reflectionString = "reflectionString"; public ReflectionUsage() {
} public String getReflectionString() {
return "reflectionString";
}
}

构造函数中初始化:

public class ReflectionUsage {
private final String reflectionString = "reflectionString"; public ReflectionUsage() {
} public String getReflectionString() {
return this.reflectionString;
}
}

可以看出跟基本类型相似,直接初始化时,编译器会将reflectionString出现的地方,替换成常量值,而构造函数中初始化不替换。

再看下对Integer的修改:

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {
private final Integer age=18; public Integer getAge(){
return age;
}
}

测试代码:

import java.lang.reflect.Field;

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionTest {
public static void main(String[] args){
try {
Class reflectionUsage = Class.forName("practise.practise.ReflectionUsage");
ReflectionUsage o = (ReflectionUsage)reflectionUsage.newInstance();
Field reflectionString = reflectionUsage.getDeclaredField("age");
reflectionString.setAccessible(true);
reflectionString.set(o,68);
reflectionString.setAccessible(false);
System.out.println(o.getAge());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}catch (IllegalAccessException | InstantiationException e){ }
}
}

运行结果:

可以看出直接初始化private final Integer age变量后,不像基本类型和String类型一样不能修改,原因是什么呢,看下class文件。

public class ReflectionUsage {
private final Integer age = Integer.valueOf(18); public ReflectionUsage() {
} public Integer getAge() {
return this.age;
}
}

可以看到编译器并未将getAge中变量替换成常量。

看到对Integer赋值时,编译器进行了自动装箱,那如果初始化String变量时,使用String.value()方式时,能否被修改呢,来看一下。

/**
* @author Cool-Coding 2018/5/15
*/
public class ReflectionUsage {
private final String reflectionString = String.valueOf("reflectionString"); public ReflectionUsage() {
} public String getReflectionString() {
return this.reflectionString;
}
}

运行结果:newReflectionString

可以看出,reflectionString的值被修改了,看下class文件:

package practise.practise;

public class ReflectionUsage {
private final String reflectionString = String.valueOf("reflectionString"); public ReflectionUsage() {
} public String getReflectionString() {
return this.reflectionString;
}
}

可见编译器并未替换getReflectionString方法中的变量。

如果在构造函数中初始化Integer类型变量呢?

public class ReflectionUsage {
private final Integer age; public ReflectionUsage() {
this.age=18;
} public Integer getAge() {
return this.age;
}
}

运行结果:

age值也被改变了,看下class文件:

public class ReflectionUsage {
private final Integer age = Integer.valueOf(18); public ReflectionUsage() {
} public Integer getAge() {
return this.age;
}
}

生成的class文件与直接初始化是一样的。

总结:

final可以修改类,变量,方法,表示不可继承,不可修改,不可覆盖(override),这里讨论了使用反射修改private final修饰的成员变量情况,当private final修改直接初始化的基本类型或String(注意不能使用String.valueOf()初始化)时,使用反射无法修改变量值,其它情况下可以修改。这也告诉我们在平时开发时,如果想定义常量请在基本类型int或String类型前加private final修饰,这样编译器会在编译时将这些变量出现的地方替换成常量,以免被恶意修改。

Java反射-修改private final成员变量值,你知道多少?的更多相关文章

  1. java 反射实现2个int变量值的交换

    import java.io.*;import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; ...

  2. Java反射-修改字段值, 反射修改static final修饰的字段

    反射修改字段 咱们从最简单的例子到难, 一步一步深入. 使用反射修改一个private修饰符的变量name 咱们回到主题, 先用反射来实现一个最基础的功能吧. 其中待获取的name如下: public ...

  3. MFC一个类访问另一个类成员对象的成员变量值

    MFC中一个类要访问另外一个类的的对象的成员变量值,这就需要获得原来那个类对象的指针,其实有好几种方法都可以实现. 比如维护一个单例模式.设置静态变量等等.我们这里举个列子,实现多个类之间的相互访问. ...

  4. MFC 一个类訪问还有一个类成员对象的成员变量值

    作者:卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/35263857 MFC中一个类要訪问另外一个类的的对象的成员变量值,这就须要获得 ...

  5. 通过JAVA反射修改JDK1.6*当中DNS缓存内容

    时间 2012-05-28 17:09:03  Taobao QA Team原文  http://qa.taobao.com/?p=15523 为了实现性能压测时的域名动态绑定功能,尝试通过java反 ...

  6. Java 反射修改类的常量值、静态变量值、属性值

    前言 有的时候,我们需要修改一个变量的值,但变量也许存在于 Jar 包中或其他位置,导致我们不能从代码层面进行修改,于是我们就用到了下面的场景,通过反射来进行修改变量的值. 定义一个实体类 class ...

  7. 反射修改 static final 变量

    一.测试结论 static final 修饰的基本类型和String类型不能通过反射修改; 二.测试案例 @Test public void test01() throws Exception { s ...

  8. java反射修改静态方法的值setAccessible

    这几天闲来无事.在网上看了一个题目,相信大家都知道这个题目  static void change(String str){         str="welcome";     ...

  9. java反射修改final变量

    private void updateFinalModifiers(Field field) throws NoSuchFieldException, IllegalAccessException { ...

随机推荐

  1. OpenCV特征点检测算法对比

    识别算法概述: SIFT/SURF基于灰度图, 一.首先建立图像金字塔,形成三维的图像空间,通过Hessian矩阵获取每一层的局部极大值,然后进行在极值点周围26个点进行NMS,从而得到粗略的特征点, ...

  2. Android群英传笔记——第八章:Activity与Activity调用栈分析

    Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命 ...

  3. REHL5上安装salt-minion

    REHL5上安装salt-minion 本文适用于rhel5.4, 6.4, 7. 仅以el5.4为例. 1 在线安装方式极为简单: # wget --no-check-certificate -O ...

  4. android动画介绍之 自定义Animation动画实现qq抖一抖效果

    昨天我们介绍了Animation的基本用法.小伙伴们了解的怎么样了?如果还没有了解过Animation的小伙伴可以看看这篇博客 android动画介绍--Animation 实现loading动画效果 ...

  5. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  6. 基于MT6752/32平台 Android L版本驱动移植步骤

    基于MT6752/32平台 Android L版本驱动移植步骤 根据MK官网所述,在Android L 版本上Turnkey ABS 架构将会phase out,而Mediatek Turnkey架构 ...

  7. 安卓系统启动脚本init.rc说明文件readme.txt翻译

    本说明文件位于system/core/init/readme.txt 本文参考深入解析安卓系统一书,进行翻译,版权部分归书的作者  刘超,资深Android专家,系统架构师. 博客地址:http:// ...

  8. 实现去哪儿来回机票选择的view

    最近有个控件是实现和去哪儿和阿里旅行的app的选择日历效果,反编译没有效果的情况下我自己实现了个,大致的原理是: 上面是产品需要实现的效果,我看了下不就是一个ListView+gridView就能实现 ...

  9. obj-c编程10:Foundation库中类的使用(6)[线程和操作队列]

    任何语言都不能避而不谈线程这个东东,虽然他是和平台相关的鸟,虽说unix哲学比较讨厌线程的说...线程不是万能灵药,但有些场合还是需要的.谈到线程就不得不考虑同步和死锁问题,见如下代码: #impor ...

  10. JQuery(二)---- JQ的事件与动画详解

    JQuery的事件 /** * 1.事件绑定的快捷方式: */ $("button:eq(0)").dblclick(function(){ alert("hahaah& ...