matt helliwell matt helliwell - 4 months ago 6x
Java Question

Why does reflection fail to update a static field?

Could someone explain why the following code fails? I have the following five classes:

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.setInt(field, field.getModifiers() & ~Modifier.FINAL);

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


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

public void log() {

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);

What the code is trying to do is to use reflection to replace the LOG variable in the ClassWithFinalFields class. As it stands, the class throws an
when it tries to set the field at the end of

However, if I replace

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


Logger oldLogger = classWithFinalFields.LOG;

then the code runs without problems and prints log "MockLogger: hello" as expected.

So the question is, why does reading the final field through reflection stop the program working? It looks like the final modifier can no longer be removed so you get an IllegalAccessException but I have no idea why. I can speculate that may be it is something to do with the compiler optimisation or classloader ordering but, despite having had a look at the byte code, I've no real idea what is going on.

If people are wondering why I would want to do this at all, it started off as looking for a way to mock out some awkward logging during unit tests whilst we were upgrading some software. Now I'm just curious as to what on earth is going on under the covers.

If anyone want to see it, the stack trace is

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(
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(
at java.lang.reflect.Field.set(
at org.matthelliwell.reflection.TestReplaceLogger.main(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(
at sun.reflect.DelegatingMethodAccessorImpl.invoke(
at java.lang.reflect.Method.invoke(
at com.intellij.rt.execution.application.AppMain.main(


You are accessing the field object bypassing its public api. Of course, anything may happen if you do that. In particular, different implementations of the Java API may behave differently.

In the Oracle JDK, Field assumes the modifiers to be final, and therefore caches the fieldAccessor (see Field.getFieldAccessor()). You have changed the modifiers, but not invalidated that cache, causing the old field accessor to be used, which still believes the field to be final.