我们指定java 使用final修饰符修饰的属性的值不能修改或其引用的地址不可修改,那么使用反射能否进行修改呢?答案是肯定的,但是却并没有那么简单. 笔者还想尝试为属性动态添加final 修饰符, 以实现类似只读变量,但是由于java对静态变量的编译机制问题,并不能实现.
1. 反射工具类
笔者开发一个简答的ReflectUtil, 实现为属性新增或删除final 修饰符
/**
* 反射工具类
*/
public class ReflectUtil {
/**
* 设置field的final修饰符
* @param field
* @param isFinal 是否使用final 修饰
* @auth zongf
* @date 2019-05-13
*/
public static void setModifierFinal(Field field, boolean isFinal) {
int modifiers = field.getModifiers();
try {
if (isFinal) {
// 设置为final
if (!Modifier.isFinal(modifiers)) {
Field aClass = field.getClass().getDeclaredField("modifiers");
aClass.setAccessible(true);
aClass.setInt(field, field.getModifiers() | Modifier.FINAL);
aClass.setAccessible(false);
}
}else {
// 接触final
if (Modifier.isFinal(modifiers)) {
Field aClass = field.getClass().getDeclaredField("modifiers");
aClass.setAccessible(true);
aClass.setInt(field, field.getModifiers() & ~Modifier.FINAL);
aClass.setAccessible(false);
}
}
System.out.println("isFinal:" + Modifier.isFinal(field.getModifiers()));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
2. 静态常量类
定义静态常量类,用于测试.定义两个静态属性, 一个使用final 修饰, 一个不使用final 修饰。
public class ProxyConstant {
public static final String host = "127.0.0.1";
public static String port = "8080";
}
3. 测试-反射修改final属性的值
@Test
public void test() throws NoSuchFieldException, IllegalAccessException {
System.out.println("原: host=" + ProxyConstant.host);
Class clz = ProxyConstant.class;
Field host = clz.getDeclaredField("host");
ReflectUtil.setModifierFinal(host, false);
host.set(null, "localhost");
System.out.println("新: host=" + ProxyConstant.host);
}
4. 测试-反射添加final 修饰符
笔者想动态为属性添加final修饰符,然后实现属性只读功能。但是发现动态添加final修饰符后,只有在反射修改时才能限制住,直接正向赋值并不生效。大概应该时静态变量的赋值在编译时有特殊处理吧, 特此记录一下.
4.1 静态更改值
动态设置属性为final之后, 静态设置值不会报错,且会修改成功。这个大概是静态变量的编译有所区别吧
@Test
public void test2() throws NoSuchFieldException, IllegalAccessException {
System.out.println("原: port=" + ProxyConstant.port);
Class clz = ProxyConstant.class;
Field port = clz.getDeclaredField("port");
ReflectUtil.setModifierFinal(port, true);
// 静态设置时不会报错
ProxyConstant.port = "80";
System.out.println("新: port=" + ProxyConstant.port);
}
4.2 动态更改值
动态设置属性为final之后, 动态设置值会报错,不能修改
@Test(expected = java.lang.IllegalAccessException.class)
public void test3() throws NoSuchFieldException, IllegalAccessException {
System.out.println("原: port=" + ProxyConstant.port);
Class clz = ProxyConstant.class;
Field port = clz.getDeclaredField("port");
ReflectUtil.setModifierFinal(port, true);
// 动态设置值时会报错
port.set(null,"80");
}