大家都知道使用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. 理解WebKit和Chromium:Chromium资源磁盘缓存

    转载请注明原文地址:http://blog.csdn.net/milado_nju ## 概述 想象一下,如果没有磁盘缓存的世界.当用户访问网页的时候,每次浏览器都需要从网站下载网页,图片,JS等资源 ...

  2. 服务端技术进阶(六)Ant和Maven的作用是什么?两者之间功能、特点有哪些区别?

    服务端技术进阶(六)Ant和Maven的作用是什么?两者之间功能.特点有哪些区别? Ant和Maven都是基于Java的构建(build)工具.理论上来说,有些类似于(Unix)C中的make ,但没 ...

  3. Android Studio 1.2.2设置显示行号

    Android Studio设置显示行号的方法与Eclipse有少许差别,直接在代码中右键,弹出右键菜单是没有显示行号功能的. 在Android Studio中设置方法有二: 1.临时显示行号 在单个 ...

  4. Process Order API - How To Scripts

    In this Document   Purpose   Questions and Answers   References APPLIES TO: Oracle Order Management ...

  5. PS图层混合算法之一(不透明度,正片叠底,颜色加深,颜色减淡)

    下列公式中,A代表了上面图层像素的色彩值(A=像素值/255),B代表下面图层像素的色彩值(B=像素值/255),C代表了混合像素的色彩值(真实的结果像素值应该为255*C).该公式也应用于层蒙板. ...

  6. 推荐一些用CRF做图像语义分割的资源

    原文地址:http://blog.sina.com.cn/s/blog_5309cefc01014nri.html 首先是code,以前找了很多,但发现比较好用的有: 1. Matlab版的UGM:h ...

  7. 初涉IPC,了解AIDL的工作原理及使用方法

    初涉IPC,了解AIDL的工作原理及使用方法 今天来讲讲AIDL,这个神秘的AIDL,也是最近在学习的,看了某课大神的讲解写下的blog,希望结合自己的看法给各位同价通俗易懂的讲解 官方文档:http ...

  8. Linux - 动态(Dynamic)与静态(Static)函数库

    首先我们要知道的是,函式库的类型有哪些?依据函式库被使用的类型而分为两大类,分别是静态 (Static) 与动态 (Dynamic) 函式库两类. 静态函式库的特色: 扩展名:(扩展名为 .a)   ...

  9. navicat为mysql建立索引

    索引的目的是大大提高查询效率,还有读写效率. kettle向sql里面插入,更新时,也要建立索引,可以大大提升处理时间. 但是建立索引报错:Specified key was too long; ma ...

  10. iOS监听模式系列之对APNs的认知与理解

    前言: APNs 协议在近两年的 WWDC 上改过两次, 15 年 12 月 17 日更是推出了革命性的新特性.但在国内传播的博客.面试题里关于 APNs 的答案全都是旧的.错的. 导航: 对 APN ...