class Foo
{
public final int finalInt = 1;
}
{
Foo foo = new Foo();
System.out.println("initial:"+foo.finalInt); // Prints '1'
Field f = foo.getClass().getField("finalInt");
f.setAccessible(true); // Allowed (!)
f.setInt(foo, 2);
System.out.println("after set:"+foo.finalInt); // Prints '1' - ah, so setInt ignored then...
System.out.println("from field:"+f.getInt()); // Prints '2' - wtf?
}
It’s fair to say that whatever I was expecting, that wasn’t it. I was either expecting setAccessible() on a ‘final’ field to throw an exception, or for it to work and for foo.finalInt to actually be changed. In reality it appears that the supposedly final value does get changed internally, but that change isn’t visible to external code.
Anyone know what’s actually going on here? I suspect the VM has decided that since finalInt is final it can keep it cached, but it would be interesting to know quite how it’s doing that.

But I simply would assume the JIT to inline the final field, so if you change it via reflection, the field is changed, but the inlined values are not. Maybe this could be verified with -Xint while executing the testcase (can’t try it for myself atm).