有人可以解釋爲什麼下面的代碼失敗?我有以下五類:爲什麼反射無法更新靜態字段?
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類。就目前而言,當它試圖在TestReplaceLogger
的末尾設置字段時,該類會拋出IllegalAccessException
。
但是,如果我更換
Logger oldLogger = (Logger)field.get(null);
與
Logger oldLogger = classWithFinalFields.LOG;
然後代碼運行沒有問題,而且打印日誌 「MockLogger:你好」 如預期。
所以問題是,爲什麼通過反射來讀取最終字段會停止程序的工作?它看起來像最後一個修飾符不能再被刪除,所以你得到一個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)
你能張貼堆棧跟蹤? – Fildor
我已將堆棧跟蹤添加到問題 –