Explain Codes LogoExplain Codes Logo

Change private static final field using Java reflection

java
reflection
best-practices
security
Alex KataevbyAlex Kataev·Jan 17, 2025
TLDR

To modify a private static final field using Java reflection, first gain access to it and then make it mutable. This can be achieved by using the Field class to override its encapsulation with setAccessible(true), and removing the final modifier using the modifiers field from the Field.class. Once the modifier is removed, you can assign a new value to the field.

try { Field field = YourClass.class.getDeclaredField("FIELD_NAME"); Field modifiersField = Field.class.getDeclaredField("modifiers"); field.setAccessible(true); // Like picking a lock... modifiersField.setAccessible(true); // Found another one... //Don't mind me, just doing some bitwise magic to unset the final modifier! modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, "NEW_VALUE"); // Now, let's make some changes... } catch (Exception e) { e.printStackTrace(); // Something unexpected happened. Time to debug! }

Always proceed with caution as this approach breaks Java's encapsulation and may lead to unpredictable results. Only use this method when you have no other options.

Reflection: a rescue tool

Reflection becomes necessary when you're dealing with legacy code or a third-party library and you can't modify the source code. However, it's recommended to first look for an API provided by the library itself - this often offers a more elegant solution.

Be aware of reflection risks

Bear in mind, reflection is a double-edged sword - it can be both useful and harmful. A private static final field could be a compile-time constant, and changing it could lead to unpredictable behavior since JVM inlines these constants. This means that any changes you make won't be noticeable wherever the constant was already used.

Mind the security

A SecurityManager could throw an exception if it detects that reflection is used to manipulate classes. Therefore, always ensure your modifications don't violate security policies, and be aware of the implications of changing access permissions.

Better alternatives

Before you pull out the reflection card, evaluate the risks it carries, such as breaking singleton properties, introducing hidden bugs, and complicating debugging.

Here are the alternatives you could consider:

  • Look for setter methods or configuration options for that field.
  • If you're modifying System fields, utilize System.setOut() or System.setErr().
  • Libraries like jOOR or Apache Commons Lang's FieldUtils, provide a cleaner and safer way to work with reflection.

Reflective changes scope and implications

Remember that reflective changes may not propagate everywhere in your code, especially in a complex codebase with multiple class loaders. Also watch out for serialization issues: after deserialization, the final field might revert to the original value, neglecting the changes.

Finer details about final fields

Manipulating final fields contradicts the core principles of Java, and may violate the Java Language Specification. Always act responsibly and be aware of the consequences of modifying the inner workings of a class.

Decompiling and its benefits

Unsure if a field is a compile-time constant? Try decompiling the class to check if its value is directly used. Decompiling tools like JD-GUI or Procyon could come in handy for this.