Browse documentation
Documentation / Protection passes / Method & field renaming

Method & field renaming

ConditionalPrivate members are always renamed. Non-private members are renamed only within a fully closed class hierarchy.

What it does

Renames method and field names, distinct from and layered on top of class name obfuscation. Renamed members use a distinct $$-prefixed scheme (for example $$f0, $$v10) so they read differently from renamed class names in decompiled output.

Private members: always renamed

A private member cannot be inherited, overridden, or referenced from outside its declaring class. There is no virtual-dispatch ambiguity to resolve, so every private method and field is renamed by default. The one exception is Java 11's nestmates feature (JEP 181), which lets classes in the same "nest" (a top-level class and its nested and inner classes) reference each other's private members directly, with no synthetic bridge accessor. A class that is a nest member or a nest host is excluded from private-member renaming, since a single-class scan can't safely resolve those references.

Non-private members: only within a closed hierarchy

Non-private, non-static instance members are also renamed, but only where the member's entire ancestor chain (every superclass and interface, recursively) resolves to classes RJNI parsed as part of this JAR and, under trial mode, selected for protection. A class with any external supertype, such as a JDK class or a third-party library type, keeps the private-only slice instead. Renaming it could silently break dispatch from code this pass cannot see.

Where a hierarchy is closed, renaming groups an interface's abstract declaration together with every implementor's override under one shared new name, so virtual dispatch stays correct across the whole group. The instance methods of java.lang.Object (equals, hashCode, toString, clone, finalize, getClass, notify, notifyAll, wait) are always excluded by signature, since every class's implicit ancestor is Object and these are reachable from arbitrary external code. static members are never renamed by this pass. Which class a compiler resolves a subclass-qualified static reference to isn't guaranteed to be the same across compiler versions, and this pass has no classpath to check it against.

A member whose name is referenced anywhere as a plain string constant is left untouched, the same reflection safety net class renaming uses.

Threat model

Where it applies, it removes your method and field names from decompiled output the same way class renaming removes package and class names. A class kept on the private-only slice (any class with an external supertype) still shows its non-private member names. This follows from the correctness boundary above.

Verified. On an unmodified Apache Commons Exec build, private renaming alone covered private fields across multiple classes. Extending to the closed-hierarchy slice produced 110 rename events. Disassembling the output confirmed that public instance methods no longer survive as raw constant-pool strings, while every Object-method override and every reflection-referenced method name was left untouched.