当前位置: 移动技术网 > IT编程>开发语言>Java > Java反射如何有效的修改final属性值详解

Java反射如何有效的修改final属性值详解

2019年07月19日  | 移动技术网IT编程  | 我要评论
前言 以前写过一篇 java 反射修改 final 属性值,本文将在这里重新温习一下java反射如何有效的修改final属性值,下面话不多说了,来一起看看详细的介绍:

前言

以前写过一篇 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 就行了。

总结

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

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网