5

Based on Change private static final field using Java reflection, I tried to set a private static final field.

(I know this is terribly hacky, but this question is not about code quality; it's about Java reflection.)

import java.lang.reflect.Field; import java.lang.reflect.Modifier; class Main { static class Foo { private static final int A = 1; int getA() { return A; } } public static void main(String[] args) throws Exception { Field modifiers = Field.class.getDeclaredField("modifiers"); modifiers.setAccessible(true); Field field = Foo.class.getDeclaredField("A"); field.setAccessible(true); modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, 2); System.out.println(new Foo().getA()); // should print 2 } } 

This prints

1 

I've tried this with OpenJDK 6 and 7, and Oracle 7.

I don't know what guarantees Java reflection gives. But if it failed, I thought there would be an Exception (practically all the reflection methods throw exceptions).

What is happening here?

2

2 Answers 2

9

Java inlines final fields that are initialized to constant expressions at compile time.

According to the Java Language Specification, any static final* field initialized with an expression that can be evaluated at compile time must be compiled to byte code that "inlines" the field value. That is, no dynamic link will be present inside class Main telling it to obtain the value for A from InterfaceA at runtime.

Decompile the bytecode and you'll find that the body of getA() simply pushes the constant 1 and returns it.


* - The JavaWorld quote says static final. Kumar points out that the static is not required by the language specification in the definition of a constant variable. I think Kumar is right and JavaWorld is in error.

Sign up to request clarification or add additional context in comments.

7 Comments

+1 OP, it didn't fail. Try System.out.println(field.get(null)); // should print 2.
@SotiriosDelimanolis, that's an excellent test to elucidate interpreter behavior.
@MikeSamuel I think it should work for any final variable initialized with an expression that can be evaluated at compile time,static is not a requirement
@KumarAbhinav, I think you're right: JLS 4.12.4 says "A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable" without any reference to static.
@PaulDraper, nice. I hadn't seen those caveats re reordering of reads and writes to final fields that occur outside the constructor.
|
0

There are cases where java does not inline static final constants. And the reflection behavior is different between Java versions (such as getting modifiers field will fail in modern Java), however there's something that stayed consistent between Java versions - sun.misc.Unsafe.

You can use it to modify value of any field and bypass all of access modifiers.

 public static void setStaticObjectUnsafe(final Field field, Object value) { final Object staticFieldBase = theUnsafe.staticFieldBase(field); final long staticFieldOffset = theUnsafe.staticFieldOffset(field); theUnsafe.putObject(staticFieldBase, staticFieldOffset, value); } 

For primitives it's the same except you need to replace value parameter and putObject with it's alternative for primitive type:

 public static void setStaticIntegerUnsafe(final Field field, int value) { final Object staticFieldBase = theUnsafe.staticFieldBase(field); final long staticFieldOffset = theUnsafe.staticFieldOffset(field); theUnsafe.putInt(staticFieldBase, staticFieldOffset, value); } 

To get unsafe instance see this solution.

If you are using Eclipse IDE you will need to tweak compiler settings in preferences: Java -> Compiler -> Errors/Warnings -> Set "Forbidden reference (access rules)" to Ignore

13 Comments

And again an other solution that does not work.
Using code you provided does in fact print 100 on my side. Tested with Java 5, 6, 8 and 17
Did you read the output on ideone? It is 10. Not 100. 10. So, there is at least one platform where it does not work. (PS.: It also doesn't work on my machine.)
What java distro did you use and what version?
The code you provided failed on all java versions I tested it with when using the code I linked. Did you run it in debug mode? Did you make any changes to my code?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.