Java反射机制可以动态修改实例中final修饰的成员变量吗?

本文探讨了Java反射机制是否能够动态修改final修饰的成员变量。实验表明,如果final变量在声明时已初始化,则其值无法被修改;若未初始化,则可通过反射机制进行动态修改。

(转自:https://www.cnblogs.com/damonhuang/p/5421563.html 侵删)

问题:Java反射机制可以动态修改实例中final修饰的成员变量吗?

回答是分两种情况的。

1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了。

2. 当final修饰的成员变量在定义的时候并没有初始化值的话,那么就还能通过java反射机制来动态修改它的值。

 

实验:

1. 当final修饰的成员变量在定义的时候就初始化了值

1 public Class Person {
2      private final String name = "damon.huang";
3      public String getName() {
4           return this.name;
5      }
6 }
复制代码
 1 import java.lang.reflect.Field;
 2 
 3 /**
 4  * @author damon.huang
 5  *
 6  */
 7 public class TestReflect {
 8 
 9     public static void main(final String[] args) {
10         final Person p = new Person();
11         System.out.println("原始值:" + p.getName());
12         System.out.println("--------separate----------");
13         changePorperty(p);
14     }
15 
16     public static void changePorperty(final Person p) {
17         final Class<?> clz = p.getClass();
18         try {
19             final Field nameField = clz.getDeclaredField("name");
20             nameField.setAccessible(true);
21             nameField.set(p, String.valueOf("huang.damon"));
22             System.out.println("反射机制修改后的Field实例的值:" + nameField.get(p));
23             System.out.println("反射机制修改后的Person实例的值:" + p.getName());
24             System.out.println("--------separate----------");
25         } catch (final NoSuchFieldException e) {
26             e.printStackTrace();
27         } catch (final SecurityException e) {
28             e.printStackTrace();
29         } catch (final IllegalArgumentException e) {
30             e.printStackTrace();
31         } catch (final IllegalAccessException e) {
32             e.printStackTrace();
33         }
34     }
35 }
复制代码

结果如下:

原始值:damon.huang
--------separate----------
反射机制修改后的Field实例的值nameField.get(p):huang.damon
反射机制修改后的Person实例的值p.getName():damon.huang
--------separate----------

为什么p.getName()的值没有被修改掉呢,这是因为

编译期间final类型的数据自动被优化了,即:所有用到该变量的地方都被替换成了常量。所以 get方法在编译后自动优化成了return "damon.huang"; 而不是 return this.name;

可以通过String name = (null!=null?"ddd":"damon.huang");来阻止编译时的自动优化。refere to: 答案引用这是的结果就会看到变量值被动态修改了

原始值:damon.huang
--------separate----------
反射机制修改后的Field实例的值:huang.damon
反射机制修改后的Person实例的值:huang.damon
--------separate----------

 

2.当final修饰的成员变量在定义的时候并没有初始化值的话

复制代码
 1 public class Person {
 2     private final String name;
 3 
 4     public Person() {
 5         this.name = "damon.huang";
 6     }
 7 
 8     public String getName() {
 9         return this.name;
10     }
11 }
复制代码
原始值:damon.huang
--------separate----------
反射机制修改后的Field实例的值:huang.damon
反射机制修改后的Person实例的值:huang.damon
--------separate----------

结果是final修饰的成员变量值被动态修改了

<think>好的,我现在需要回答用户关于Javafinal修饰成员变量和类变量的规则及要求的问题。首先,我需要回顾用户提供的引用内容,确保答案准确并涵盖所有关键点。用户引用了多个资料,其中提到final修饰变量的不同情况。例如,引用3指出final变量一旦初始化后值不能改变,用于常量定义、保证不变性和优化性能。引用4提到成员变量必须在创建对象前赋值,可以通过定义赋值、构造器赋值或set方法(但根据Java规则,set方法可能不适用,因为final变量只能赋值一次)。引用5强调final修饰成员变量必须显式初始化,否则编译错误。 接下来,我需要区分成员变量和类变量(即静态变量)。成员变量属于实例,而类变量属于类。对于final成员变量初始化方式包括定义赋值、构造器赋值或初始化块。而对于final变量,必须在定义或在静态初始化块中赋值,因为它们在类加载初始化,早于对象创建。 需要注意,用户可能混淆了成员变量和类变量初始化要求。例如,类变量不能在构造器中初始化,因为构造器属于实例层面。此外,正确使用final可以确保线程安全或不可变对象,这也是用户可能关心的应用场景。需要验证每个规则是否符合Java规范,比如是否允许在方法中为final变量赋值(不允许,只能在声明、构造器或初始化块中)。同,引用4提到三种赋值方式,但set方法可能不适用,因为final变量只能赋值一次,而set方法可能在对象生命周期中被多次调用,导致编译错误。 总结,需分点列出成员变量和类变量的规则,强调初始化机和赋值方式的不同,并举例说明。此外,相关问题应引导用户深入理解final的应用,如内存模型影响、设计模式中的使用等。</think>### Javafinal修饰成员变量和类变量的规则及要求 #### 一、final修饰成员变量实例变量) 1. **初始化要求** - final成员变量必须在对象创建**前**完成初始化[^4][^5]。 - 初始化方式: - **定义直接赋值**:`private final int value = 10;` - **构造方法中赋值**:所有构造方法必须包含对该变量的赋值[^4]。 - **实例初始化块中赋值**:`{ value = 10; }` ```java public class Example { private final int a = 5; // 方式1:定义赋值 private final int b; private final int c; { c = 10; } // 方式3:实例初始化块赋值 public Example(int b) { this.b = b; // 方式2:构造方法赋值 } } ``` 2. **不可修改性** - 一旦赋值后,值不可更改,否则编译报错[^3][^4]。 3. **禁止setter方法** - 若通过普通方法(如setter)赋值,会导致编译错误,因为final变量只能被赋值一次[^4]。 --- #### 二、final修饰变量(静态变量) 1. **初始化要求** - final变量必须在**类加载**完成初始化[^3][^5]。 - 初始化方式: - **定义直接赋值**:`private static final int MAX = 100;` - **静态初始化块中赋值**:`static { MAX = 100; }` ```java public class Constants { private static final int MAX; // 方式1:定义赋值 static { MAX = 100; // 方式2:静态初始化块赋值 } } ``` 2. **不可修改性** - 与成员变量相同,初始化后值不可变[^3]。 --- #### 三、特殊规则 1. **对象类型变量** - final修饰对象变量,**引用不可变,但对象内部属性可修改**[^2][^3]。 例如: ```java final List<String> list = new ArrayList<>(); list.add("Hello"); // 允许操作对象内部 list = new ArrayList<>(); // 编译错误:引用不可更改 ``` 2. **编译常量优化** - 若final变量是基本类型或字符串且赋值表达式为常量,编译器会直接替换值到使用位置[^3]。 --- #### 四、对比总结 | 类型 | 初始化机 | 允许的初始化方式 | 作用范围 | |------------|--------------------|--------------------------------|--------------| | 成员变量 | 对象创建前 | 定义赋值、构造方法、实例块 | 对象实例级别 | | 类变量 | 类加载 | 定义赋值、静态初始化块 | 类级别 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值