gpt4 book ai didi

java - 为什么反射无法更新静态字段?

转载 作者:搜寻专家 更新时间:2023-10-31 19:33:42 26 4
gpt4 key购买 nike

有人可以解释为什么下面的代码会失败吗?我有以下五个类(class):

public class TestReplaceLogger {
public static void main(String[] arv) throws Exception {
ClassWithFinalFields classWithFinalFields = new ClassWithFinalFields();
Field field = ClassWithFinalFields.class.getDeclaredField("LOG");

// Comment this line and uncomment out the next line causes program work
Logger oldLogger = (Logger)field.get(null);
//Logger oldLogger = classWithFinalFields.LOG;


Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

field.set(null, new MockLogger());

classWithFinalFields.log();
}
}

public class ClassWithFinalFields {
public static final Logger LOG = new RealLogger();

public void log() {
LOG.log("hello");
}
}

public interface Logger {
public void log(String msg);
}

public class RealLogger implements Logger{
public void log(String msg) {
System.out.println("RealLogger: " + msg);
}
}

public class MockLogger implements Logger {
public void log(String msg) {
System.out.println("MockLogger: " + msg);
}
}

代码试图做的是使用反射来替换 ClassWithFinalFields 类中的 LOG 变量。目前,该类在尝试在 TestReplaceLogger 末尾设置字段时抛出 IllegalAccessException

但是,如果我替换

Logger oldLogger = (Logger)field.get(null);

Logger oldLogger = classWithFinalFields.LOG;

然后代码运行没有问题,并按预期打印日志“MockLogger:hello”。

那么问题来了,为什么通过反射读取 final 字段会停止程序运行?看起来 final 修饰符不能再被删除,所以你得到一个 IllegalAccessException 但我不知道为什么。我可以推测这可能与编译器优化或类加载器排序有关,但尽管看过字节码,但我并不清楚发生了什么。

如果人们想知道我为什么要这样做,它开始是在我们升级某些软件时寻找一种方法来模拟单元测试期间一些笨拙的日志记录。现在我很好奇幕后到底发生了什么。

如果有人想看,堆栈跟踪是

Exception in thread "main" java.lang.IllegalAccessException: Can not set static final org.matthelliwell.reflection.Logger field org.matthelliwell.reflection.ClassWithFinalFields.LOG to org.matthelliwell.reflection.MockLogger
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:73)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:77)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:741)
at org.matthelliwell.reflection.TestReplaceLogger.main(TestReplaceLogger.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

最佳答案

您正在绕过其公共(public) API 访问字段对象。当然,如果你这样做,任何事情都可能发生。特别是,Java API 的不同实现可能会有不同的行为。

在 Oracle JDK 中,Field 假定修饰符是最终的,因此缓存 fieldAccessor(参见 Field.getFieldAccessor())。您已更改修饰符,但未使该缓存无效,导致使用旧的字段访问器,它仍然认为该字段是最终的。

关于java - 为什么反射无法更新静态字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19045209/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com