前言

以前写过一篇 Java 反射修改 final 属性值,本文将在这里重新温习一下Java反射如何有效的修改final属性值,下面话不多说了,来一起看看详细的介绍:

假设有个类

class Person {

public final String name = "Mike";

}

这里声明 name 为非静态的属性只是为了说明反射修改 final 属性无关乎静态不静态,静态只是表现在它是一个类属性,在一个类加载器空间只会有一份拷贝,仅此而已。

创建一个通用方法进行反射修改属性值

public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {

Field field = object.getClass().getDeclaredField(fieldName);

Field modifiersField = Field.class.getDeclaredField("modifiers");

modifiersField.setAccessible(true); //Field 的 modifiers 是私有的

modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

if(!field.isAccessible()) {

field.setAccessible(true);

}

field.set(object, newFieldValue);

}

调用 modify(...)方法试图修改 person 的 name 属性

Person person = new Person();

modify(person, "name", "Michael");

System.out.println(person.name); //输出仍然是 Mike

为什么代码执行下来没问题,但输出又还是原来的值呢?但总是可以通过反射方式获取到修改后的新值。这就是 Java 编译器对 final 属型的内联优化,即编译时把该 final 的值直接放到了引用它的地方。即使是反射修改了该属性,但这种事后处理于事无补。

所以我在标题中所说如何有效的修改 final 属性值是指:反射修改了 final 属性值后能够在后续代码中使用修改后的值。

如果 Person 有个方法

public String getName() {

return name;

}

在前面代码反射修改了 name 属性后,getName()方法也仍然是返回 "Mike"。

那么 Java 会对什么类型的 final 值进行内联编译呢?它们基本类型 byte, char, short, int, long, float, double, boolean; 再加上 Literal String 类型(直接双引号字符串)。只要是不被编译器内联优化的 final 属性都可以通过反射有效的进行修改(修改后能使用到新的值)

String 类型比较特殊, 如果把 Person 类 name 属性改成用 new String("Mike")

public final String name = new String("Mike");

就能够有效的使用上面的反射代码修改 name 的值,最后

modify(person, "name", "Michael");

System.out.println(person.name) //输出就是 Michael 了

正如基本类型有相应的包装类型,可以把 new String("Mike")想像为 "Mike" 的包装类型。也确实是基本类型的 final 包装类型可以被反射有效的修改,new String("Mike") 也可以。

但我们要是在 IDE 中写成 new String("Mike") , 可能被告知 new String("")这种格式是冗余的,IDE 又会把它改成 "Mike",重而反射行为变得不正确。

还有,如果 final 属性值是通过构造函数传入的能不能有效的被修改,当然可以,因为它也不会被编译器内联优化。

总之一句话:只要不会被编译器内联优化的 final 属性就可以通过反射有效的进行修改 -- 修改后代码中可使用到新的值

为加深印象,我们最后作一个较全面的测试

public class TestFinal {

public static void main(String[] args) throws Exception {

Person jordan = new Person("Chicago");

modify(jordan , "firstName", "Michael");

System.out.println(jordan .firstName); //Mike

modify(jordan , "lastName", "Michael");

System.out.println(jordan.lastName); //Michael

modify(jordan , "age", 51f);

System.out.println(jordan .age); //50.5

modify(jordan , "height", 1.98f);

System.out.println(jordan.height); //1.98

modify(jordan, "address", new Address("ccc", "ddd"));

System.out.println(jordan.address.line1); //ccc

modify(jordan , "city", "Miami");

System.out.println(jordan.city); //Miami

}

}

class Person {

public final String firstName = "Mike";

public final String lastName = new String("Jordan"); //可被有效修改

public final float age = 50.5f;

public final Float height = 1.99f; //可被有效修改

public final Address address = new Address("aaa", "bbb"); //可被有效修改

public final String city; //可被有效修改

public Person(String city) {

this.city = city;

}

}

final class Address {

public final String line1;

public final String line2;

public Address(String line1, String line2) {

this.line1 = line1;

this.line2 = line2;

}

}

我以前也有一人误区: 就是总以为加了 final 关键值,该属性就会被编译器内联优化,就不能用反射有效的进行修改。通过今天的梳理终于清晰的明白了:final 属性,只要不是基本类型和字面 String,就可以正常使用反射修改它的值。以前曾为了想反射修改某个 final 值还特意把 final 关键字给去掉,完全不用这么做。所以对于基本类型和字面 String, final 只是预示着它的值不能被正常的代码修改。

因此我们在声明 log 时

private static Logger log = LoggerFactory.getLogger(TestFinal.class);

private static final Logger log = LoggerFactory.getLogger(TestFinal.class);

性能上不会有什么差异,也不会因为多一个 final 而在任何使用到了 log 的地方内联它。只要留意基本类型和字面 String 就行了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对聚米学院的支持。

java反射设置属性值_Java反射如何有效的修改final属性值详解相关推荐

  1. fianl属性 java_Java反射如何有效的修改final属性值详解

    前言 以前写过一篇 Java 反射修改 final 属性值,本文将在这里重新温习一下Java反射如何有效的修改final属性值,下面话不多说了,来一起看看详细的介绍: 假设有个类 class Pers ...

  2. Java集合迭代器原理图解_Java Iterator接口遍历单列集合迭代器原理详解

    这篇文章主要介绍了Java Iterator接口遍历单列集合迭代器原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Iterator接口概述 ...

  3. java 对象重写equals方法_Java如何重写object类的equals方法详解

    1.Object类的equals()方法: 比较两个对象是否是同一个对象,equals() 方法比较两个对象,是判断两个对象引用指向的是同一个对象,即比较 2 个对象的内存地址是否相等.是则返回tru ...

  4. java 跨域上传_java使用webuploader实现跨域上传详解

    前言 项目中使用webuploader进行文件上传,需要用到跨域,查看webuploader的issues发现是支持上传的,但是他们写的回复都是不清不白的,有点迷糊:想了半天才知道咋回事,也可能是我比 ...

  5. java super父类的父类_java super关键字,super访问父类成员详解

    之情给大家介绍了一下super调用父类构造方法方面的内容,下面要接着给大家介绍的就是super访问父类成员方面的内容,分别对super调用成员属性以及super调用成员方法做了详细介绍. 在子类的成员 ...

  6. java的数组查找算法_java数组、排序算法、查找算法详解

    1.为什么定义数组要采用type[] arrayName;这种方式? 因为这种方式具有很好的可读性,使用这种方式很容易就可以理解这是定义一个变量,其中变量名是arrayName,变量的类型是type[ ...

  7. java每一个小时同步_Java同步块(synchronized block)使用详解

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

  8. java socket中的方法_Java中关于Socket的方法与作用详解

    1.java.net.Socket;套接字.封装了TCP通讯协议,使用它可以基于TCP与远端计算机上的服务端应用程序链接并进行通讯. 实例化Socket就是与服务器端建立连接的过程.这里需要传入两个参 ...

  9. java do while 循环语句_java中do...while循环语句使用方法详解

    do..while循环语句也称为后测试循环语句,它的循环重复执行方式,也是利用一个条件来控制是否要继续重复执行这个语句.与while循环所不同的是,它先执行一次循环语句,然后再去判断 - do...w ...

最新文章

  1. Php 删除数组后几个元素
  2. [20160307]绑定变量的分配长度3.txt
  3. 如何在github发布个人网站或开源项目-手把手教您制作并发布个人网站或主页(二)
  4. Task On The Board CodeForces - 1367D(思维)
  5. BZOJ4590: [Shoi2015]自动刷题机
  6. 算法竞赛进阶指南——后缀数组
  7. C语言实现DES加解密算法
  8. php验证支付回调,php对微信支付回调处理的方法(合集)
  9. Spring Boot 使用 Log4j2
  10. discuzx3.2自定义积分操作日志,discuzx积分二次开发完全记录
  11. 阶段3 1.Mybatis_02.Mybatis入门案例_3.mybatis注解开发和编写dao实现类的方式
  12. 363.矩形区域不超过K的最大数值和
  13. 基于CBOW网络手动实现面向中文语料的word2vec
  14. Tryhackme-Windows Exploitation Basics
  15. mac下如何使用ssh-copy-id完成ssh的公钥认证登录
  16. 区块链可以减少社会不平等吗?
  17. var和let的区别
  18. 《搜索》— NYOJ 82 迷宫寻宝(一)
  19. Redis的maven依赖
  20. ptable java_ttable怎么在java中编写

热门文章

  1. Water-Net:水下图像增强基准数据集(UIEB Dataset)2019年TIP顶刊论文
  2. python openpyxl读写xlsx_python高阶教程-python操作xlsx文件(openpyxl)
  3. win10电脑黑屏只有鼠标箭头_电脑开机进入系统后显示黑屏,只能看到一个鼠标指针,如何解决?...
  4. r语言x c(-1 -2),【软件】R语言入门之向量
  5. 手把手带你领略双十一背后的核心技术Sentinel之服务的熔断降级
  6. Eureka出现No instances available for xxx的五种解决方案(不能解决你骂我)
  7. 云服务器Linux jdk安装详解(centos8)
  8. Ⅰ:zookeeper的单机安装 - 详细教程
  9. c# ef报错_C#中Entity Framework常见报错汇总
  10. org.apache.hadoop.util.PlatformName //cgywin下Hadoop-0.21.0 错误问题