JAVA反射修改常量,以及其局限(修改private final限制)

本文深入解析Java反射机制在修改基本类型静态常量时的局限性,通过实例演示了基本类型如int、long、boolean及String在使用反射进行修改时的异常行为,并解释了其背后的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:PS:不过很好奇下面的那个private属性怎么被另一个类访问到的。
注:又研究了下,发现特么应该下面的修改常量的核心代码应该是在这个类的main函数里面,坑,写清楚点撒

对如下Bean类,其中的INT_VALUE是私有静态常量
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. class Bean{  
  2.     private static final Integer INT_VALUE = 100;  
  3. }  
修改常量的核心代码:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. System.out.println(Bean.INT_VALUE);  
  2. //获取Bean类的INT_VALUE字段  
  3. Field field = Bean.class.getField("INT_VALUE");  
  4. //将字段的访问权限设为true:即去除private修饰符的影响  
  5. field.setAccessible(true);  
  6. /*去除final修饰符的影响,将字段设为可修改的*/  
  7. Field modifiersField = Field.class.getDeclaredField("modifiers");  
  8. modifiersField.setAccessible(true);  
  9. modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  10. //把字段值设为200  
  11. field.set(null200);  
  12. System.out.println(Bean.INT_VALUE);  

以上代码输出的结果是:

100
200

说明用反射私有静态常量成功了。


方案的局限

注意到上述代码的中的静态常量类型是Integer——但是我们项目中实际需要修改的字段类型并不是包装类型Integer,而是java的基本类型int。
当把常量的类型改成int之后,

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. class Bean{  
  2.     private static final int INT_VALUE = 100;//把类型由Integer改成了int  
  3. }  

在其他代码都不变的情况下,代码输出的结果竟然变成了诡异的:

100
100

而且在调试的过程中发现,在第二次输出的时候,内存中的Bean.INT_VALUE是已经变成了200,但是System.out.println(Bean.INT_VALUE)输出的结果却依然时诡异的100?!

——反射失效了吗?

又试了其他几种类型,发现这种貌似失效的情会发生在int、long、boolean以及String这些基本类型上,而如果把类型改成Integer、Long、Boolean这种包装类型,或者其他诸如Date、Object都不会出现失效的情况。

原因

经过一系列的研究、推测、搜索等过程,终于发现了原因:

对于基本类型的静态常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。

参考:Modifying final fields in Java
即对于常量 public static final int maxFormatRecordsIndex = 100 ,代码

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if( index > maxFormatRecordsIndex   ){  
  2.     index  =  maxFormatRecordsIndex ;  
  3. }       

这段代码在编译的时候已经被java自动优化成这样的:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if( index > 100){  
  2.     index = 100;  
  3. }  

所以在INT_VALUE是int类型的时候

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. System.out.println(Bean.INT_VALUE);  
  2. //编译时会被优化成下面这样:  
  3. System.out.println(100);  

所以,自然,无论怎么修改Boolean.INT_VALUE,System.out.println(Bean.INT_VALUE)都还是会依然固执地输出100了。

——这本身是JVM的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。

这大概是JAVA反射的一个局限吧——修改基本类型的常量时,不是太可靠。


附一下我测试时候的DEMO吧

代码

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.lang.reflect.Field;  
  2. import java.lang.reflect.Modifier;  
  3. import java.util.Date;  
  4.   
  5. public class ForClass {  
  6.     static void setFinalStatic(Field field, Object newValue) throws Exception {  
  7.         field.setAccessible(true);  
  8.         Field modifiersField = Field.class.getDeclaredField("modifiers");  
  9.         modifiersField.setAccessible(true);  
  10.         modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  11.         field.set(null, newValue);  
  12.     }  
  13.   
  14.     public static void main(String args[]) throws Exception {  
  15.   
  16.         System.out.println(Bean.INT_VALUE);  
  17.         setFinalStatic(Bean.class.getField("INT_VALUE"), 200);  
  18.         System.out.println(Bean.INT_VALUE);  
  19.   
  20.         System.out.println("------------------");  
  21.         System.out.println(Bean.STRING_VALUE);  
  22.         setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");  
  23.         System.out.println(Bean.STRING_VALUE);  
  24.           
  25.         System.out.println("------------------");  
  26.         System.out.println(Bean.BOOLEAN_VALUE);  
  27.         setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);  
  28.         System.out.println(Bean.BOOLEAN_VALUE);  
  29.   
  30.         System.out.println("------------------");  
  31.         System.out.println(Bean.OBJECT_VALUE);  
  32.         setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());  
  33.         System.out.println(Bean.OBJECT_VALUE);  
  34.   
  35.     }  
  36. }  
  37.   
  38. class Bean {  
  39.     public static final int INT_VALUE = 100;  
  40.     public static final Boolean BOOLEAN_VALUE = false;  
  41.     public static final String STRING_VALUE = "String_1";  
  42.     public static final Object OBJECT_VALUE = "234";  
  43. }  

代码输出

100
100
------------------
String_1
String_1
------------------
false
true
------------------
234
Fri Apr 25 00:55:05 CST 2014

说明

——其中的Boolean跟Object类型常量被正确修改了,而基本类型int和String的修改则“没有生效”。

<think>嗯,用户想了解Java中枚举的设计方法和实现思路,特别是设计模式和最佳实践。首先,我得回忆一下Java枚举的基本特性。Java的枚举类型从Java 5开始引入,它们不仅仅是常量集合,还可以有方法、构造函数,甚至实现接口,这为设计模式提供了很多可能性。 用户可能已经知道枚举的基本用法,比如定义一组常量,但可能不清楚如何利用枚举来实现更复杂的设计模式。例如,单例模式通常用枚举实现,因为枚举实例默认是线程安全且单例的。我需要提到这一点,并引用Effective Java中的建议,比如Joshua Bloch提到的构建器模式,但可能更相关的在这里是单例模式的最佳实践[^1]。 另外,用户可能想知道如何在枚举中添加方法,或者如何用枚举实现策略模式。比如,每个枚举实例可以有不同行为,通过重写方法来实现。比如引用中的支付策略,可能用枚举来替代策略接口的不同实现[^5]。不过示例中的策略模式用了Spring的上下文,但枚举也可以实现类似功能,减少类的数量。 还要考虑枚举的状态管理,比如是否应该包含可变状态。通常建议枚举是不可变的,所以构造函数里初始化后不应该修改字段,这样更安全。需要提到最佳实践中推荐的做法,比如使用final字段,私有构造函数等。 另外,关于设计模式,除了单例,还有状态模式、命令模式等可以用枚举简化。比如状态机可以用枚举的不同实例表示不同状态,每个状态处理不同行为。引用中提到的复合模式和链式处理可能相关,但需要具体例子说明[^2][^4]。 可能还需要提到枚举与工厂模式的结合,比如通过枚举实例来创建对象,或者根据输入参数返回不同的枚举实例,进而执行不同的逻辑。比如引用中的支付方式选择,可能用枚举作为工厂来选择策略。 另外,需要注意枚举的局限性,比如无法继承,所以扩展性有限。但在某些场景下,枚举的优势明显,比如类型安全、易于维护等。需要提醒用户根据具体需求选择是否使用枚举。 关于实现思路,可能需要分步骤讲解:定义枚举结构、添加方法和字段、实现接口、使用枚举实现设计模式,并给出代码示例。比如单例模式的枚举实现,策略模式的具体例子,状态机的实现等。 还要考虑线程安全的问题,因为枚举实例的创建是线程安全的,所以适合多线程环境。这也是单例模式推荐使用枚举的原因之一。 可能还需要比较枚举与其他实现方式的优缺点,比如用类实现单例需要考虑双重检查锁,而枚举则自动处理了实例化和线程安全。 最后,生成的相关问题要覆盖用户可能感兴趣的扩展内容,比如性能、替代方案、实际应用案例等,帮助用户深入理解。</think>### Java枚举设计模式与最佳实践分步解析 Java枚举(`enum`)是类型安全的常量集合,支持面向对象特性。以下是设计模式实现思路和最佳实践: --- #### 一、基础设计方法 1. **基本语法** ```java public enum Color { RED, GREEN, BLUE } ``` 2. **添加属性与方法** ```java public enum Planet { EARTH(5.97e24, 6.37e6), MARS(6.42e23, 3.39e6); private final double mass; // final保证不可变 private final double radius; Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } public double surfaceGravity() { return G * mass / (radius * radius); } } ``` --- #### 二、设计模式实现 1. **单例模式(最佳实践)** ```java public enum Singleton { INSTANCE; public void doSomething() { // 线程安全且天然防反射攻击 } } ``` > *《Effective Java》推荐枚举单例,避免反射和序列化问题。* 2. **策略模式** ```java public enum Operation { ADD { public double apply(double x, double y) { return x + y; } }, SUBTRACT { public double apply(double x, double y) { return x - y; } }; public abstract double apply(double x, double y); } ``` 3. **状态机** ```java public enum OrderStatus { NEW { public void next(Order order) { order.setState(PAID); } }, PAID { public void next(Order order) { order.setState(SHIPPED); } }; public abstract void next(Order order); } ``` --- #### 三、最佳实践 1. **不可变性** - 字段声明为`final`,避免状态修改 - 构造函数私有化,禁止外部实例化 2. **接口实现** ```java public enum Logger implements Runnable { FILE_LOGGER { public void run() { /* 文件日志逻辑 */ } }, CONSOLE_LOGGER { public void run() { /* 控制台日志逻辑 */ } }; } ``` 3. **工厂模式** ```java public enum PaymentMethod { ALIPAY { public PaymentService getService() { return new AlipayService(); } }, WECHAT_PAY { public PaymentService getService() { return new WechatPayService(); } }; public abstract PaymentService getService(); } ``` 4. **集合管理** ```java public enum Day { MONDAY, TUESDAY, WEDNESDAY; public static Day fromName(String name) { return Arrays.stream(values()) .filter(d -> d.name().equalsIgnoreCase(name)) .findFirst() .orElseThrow(IllegalArgumentException::new); } } ``` --- #### 四、性能优化 1. **避免过度复杂** - 枚举实例数量应控制在合理范围(通常不超过20个) - 复杂逻辑可委托给外部类实现 2. **替代方案比较** | 场景 | 枚举适用性 | 替代方案 | |---------------------|--------------------|--------------------| | 固定常量集合 | ✅ 最佳选择 | 类常量 | | 需要多态行为 | ✅ 策略模式 | 接口+实现类 | | 频繁变更的类型 | ❌ 不适合 | 配置文件+工厂类 | --- #### 五、实际应用示例 ```java // 审批工作流状态机[^4] public enum ApprovalState { DRAFT { public void submit(Document doc) { doc.setState(REVIEW); } }, REVIEW { public void approve(Document doc) { doc.setState(FINAL); } }; public void submit(Document doc) { throw new IllegalStateException(); } public void approve(Document doc) { throw new IllegalStateException(); } } ``` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值