今天面试遇到的,发射修改属性问题,上网查了一下,有总结写的
lass Person {
public final String name = "Mike";
}
这里声明 name 为非静态的属性只是为了说明反射修改 final 属性无关乎静态不静态,静态只是表现在它是一个类属性,在一个类加载器空间只会有一份拷贝,仅此而已。创建一个通用方法进行反射修改属性值
12345678910111213 | 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 属性
123 | 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 属性就可以通过反射有效的进行修改 -- 修改后代码中可使用到新的值为加深印象,我们最后作一个较全面的测试
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 | 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 就行了。https://unmi.cc/java-reflection-modify-final-field/, 来自 隔叶黄莺 Unmi Blog
- 总结一下今天android开发遇到的问题
- 总结一下面试中遇到的问题,一共面试了4家公司。
- 总结一下今天工作中遇到的问题
- 总结一下最近面试遇到的一些问题吧
- 总结一下今天在编程中遇到的问题和相应的解决办法
- C/C++ 面试前 遇到问题总结
- 今天小小的总结一下最近的小程序中的问题
- 近期面试遇到的问题以及总结(python和openstack)
- 今天帮朋友做了一个2501帧的flash动画,遇到了些问题,总结下。----无法导出flash,系统显示内存不足
- Android--面试中遇到的问题总结(三)
- 总结一下自己Java实习后遇到的一些问题。
- 今天开始学习LNMP,遇到问题记录一下~!
- 面试遇到的问题总结(一)
- 今天有温习了一下指针,最近遇到很多指针问题,又蒙了
- 今天遇到了Cookie的问题,网上搜来些资料,记录一下备忘!
- 作DNN CrossArticle模块BUG修改时,遇到2个小问题,在此记录一下。
- 整理一下今天遇到的一些小问题
- IOS-7-纪念一下刚刚接到的第一份offer(下面是面试遇到的问题)
- 最近几次都遇到了sizeof,但是每次都有一点蒙B的感觉,今天总结一下;
- 对cookie操作的时候遇到了一些的 删除、修改、查询不了问题,看来篇文章发现不错,抓一下