Java Overriding vs Method Hiding: Key Differences
java overriding vs method hiding: Understand the difference between overriding instance methods and hiding static methods in Java, including runtime behavior, syntax r...
The question of java overriding vs method hiding comes down to one fundamental rule: instance methods are overridden, static methods are hidden. When a subclass declares an instance method with the same signature as a parent instance method, the subclass version replaces the parent version in the virtual dispatch table. When a subclass declares a static method with the same signature as a parent static method, the subclass version only hides the parent version, and the compiler decides which one to call based on the declared type of the reference.
The Core Difference in a Minimal Example
Consider this pair of classes:
class Parent { void instanceMethod() { System.out.println("Parent instance method"); } static void staticMethod() { System.out.println("Parent static method"); } } class Child extends Parent { @Override void instanceMethod() { System.out.println("Child instance method"); } static void staticMethod() { System.out.println("Child static method"); } }
Now call both methods through a Parent reference that points to a Child instance:
Parent ref = new Child(); ref.instanceMethod(); // prints "Child instance method" ref.staticMethod(); // prints "Parent static method"
The instance method call is resolved at runtime using the object's actual type, so Child's version runs. The static method call is resolved at compile time using the declared type of the reference, so Parent's version runs even though the object is actually a Child. That single behavioral difference drives everything else about the two mechanisms.
How Overriding Works in the JVM
Overriding relies on virtual dispatch. The JVM maintains a method table for each class. When the invokevirtual instruction executes, the JVM looks up the method in the method table of the object's actual class, not the declared type. This is what makes polymorphism possible.
For an override to be valid, the subclass method must satisfy the same rules as the parent method:
- The method signature must match exactly (name and parameter types).
- The return type must be the same or a covariant subtype.
- The access modifier cannot be more restrictive than the parent's.
- The method must not throw checked exceptions that are broader than those declared by the parent.
The @Override annotation is not required, but it causes a compile error if the method does not actually override a parent method, which catches typos and signature mismatches early. It also signals to other developers that the method participates in a parent-child relationship.
How Method Hiding Works
Method hiding applies to static methods. A static method belongs to the class itself, not to instances, so there is no virtual dispatch. When a subclass declares a static method with the same signature, the parent method is not replaced; it is hidden.
The key behavioral difference is visible when you call the method through a reference of the parent type:
Parent ref = new Child(); Parent.staticMethod(); // Parent version Child.staticMethod(); // Child version ref.staticMethod(); // Parent version (compile-time resolution)
The compiler resolves the call based on the static type of the reference. Even if the reference points to a Child instance, the parent's static method is invoked. This is why hiding is often described as a compile-time behavior while overriding is a runtime behavior.
Comparing the Two Behaviors Side by Side
| Aspect | Overriding | Method Hiding |
|---|---|---|
| Applies to | Instance methods | Static methods |
| Resolution time | Runtime (virtual dispatch) | Compile time |
@Override annotation | Valid and recommended | Causes compile error |
| Polymorphism | Fully supported | Not polymorphic |
| Access modifier | Cannot be more restrictive | Cannot be more restrictive |
| Return type | Same or covariant | Same or covariant |
The access modifier and return type rules are identical for both. The critical difference is the resolution mechanism: virtual dispatch for instance methods, static binding for static methods.
Common Mistakes with Static Method Hiding
A frequent mistake is assuming that a static method in a subclass will behave polymorphically. Consider this scenario:
class Service { static void log() { System.out.println("Service log"); } } class AuditService extends Service { static void log() { System.out.println("Audit log"); } }
If you write:
Service s = new AuditService(); s.log();
The output is Service log, not Audit log. Developers who expect polymorphism here are surprised. The correct way to call the subclass version is through the subclass type:
AuditService.log();
Another common mistake is adding @Override to a static method. The compiler rejects this with an error because static methods cannot be overridden. The annotation is reserved for instance methods that override a parent method.
Why the Distinction Matters for Design
Understanding the difference prevents subtle bugs in code that mixes inheritance with static utility methods. If you need polymorphic behavior, the method must be an instance method. Static methods are appropriate for operations that do not depend on instance state, but they cannot participate in polymorphism.
When designing a class hierarchy, decide early whether a method needs to behave differently in subclasses. If it does, make it an instance method and rely on overriding. If it is a pure utility that never changes behavior across subclasses, a static method is fine, but callers must be aware that hiding does not provide dynamic dispatch.
Practical Guidance for Choosing the Right Approach
Use overriding when:
- The behavior must vary based on the runtime type of the object.
- You need to extend or replace the parent implementation in subclasses.
- The method relies on instance state.
Use static methods and accept hiding when:
- The method is a utility that does not depend on instance state.
- You do not expect subclasses to provide different behavior.
- You want to call the method without an instance.
A common pattern is to keep static utility methods in a final class or a dedicated utility class rather than in an inheritance hierarchy, which avoids the hiding problem entirely. If you must put static methods in a class that is meant to be extended, document the hiding behavior explicitly so that callers do not assume polymorphism. The cost of ignoring this distinction is usually a bug that only appears when a subclass introduces a static method with the same signature, silently changing which implementation runs for callers that reference the parent type.