Java Notes
Learn method overriding in Java — how a child class replaces a parent method, the @Override annotation, rules for overriding, what cannot be overridden, and runtime dispatch.
Method overriding happens when a child class redefines a method that already exists in the parent class — same name, same parameters, same return type. The child's version replaces the parent's for child objects.
This is runtime polymorphism. Java decides at runtime which version to call, based on the actual object, not the reference type.
Basic Override Example
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Line by line:
Animal a1 = new Dog();— reference isAnimalbut object isDog.a1.makeSound()— Java looks at the actual object (Dog), not the reference type (Animal). Runs Dog's version.@Override— annotation that tells the compiler: this is intentionally overriding a parent method. If the parent does not have a matching method, the compiler throws an error. Always use it.
The @Override Annotation
@Override is not required but you should always use it.
public class Vehicle {
void start() {
System.out.println("Vehicle starting");
}
}
public class Car extends Vehicle {
// Without @Override — compiles, but no protection
void strat() { // typo — creates a new method instead of overriding
System.out.println("Car starting");
}
}
public class Car extends Vehicle {
@Override
void strat() { // COMPILE ERROR — no strat() in Vehicle to override
System.out.println("Car starting");
}
}Without @Override, a typo silently creates a new method instead of overriding. With @Override, the compiler catches the mistake immediately.
Rules for Overriding
- Same method signature — same name, same parameter list.
- Same or covariant return type — you can return a subtype (e.g., return
Dogwhere parent returnsAnimal). - Access modifier must be same or wider — a
protectedparent method can be overridden aspublic, but not asprivate. - Cannot throw broader checked exceptions — if parent throws
IOException, child cannot throwException.
public class Parent {
protected Animal getAnimal() {
return new Animal();
}
}
public class Child extends Parent {
@Override
public Dog getAnimal() { // public is wider than protected — OK
return new Dog(); // Dog is a subtype of Animal — OK (covariant)
}
}Calling the Parent Version — super.method()
Sometimes the child wants to extend the parent's behaviour rather than fully replace it.
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
super.log(message) runs the parent's version. Then the child adds the timestamp. Without super.log(), the parent's line would never print.
What Cannot Be Overridden
Some methods are blocked from overriding:
public class Base {
final void cannotOverride() { // final — locked
System.out.println("Base final method");
}
static void staticMethod() { // static — not overriding, only hiding
System.out.println("Base static");
}
private void privateMethod() { // private — not visible to child
System.out.println("Base private");
}
}
public class Derived extends Base {
// @Override
// void cannotOverride() { } // COMPILE ERROR — cannot override final
// This is METHOD HIDING, not overriding
static void staticMethod() {
System.out.println("Derived static");
}
// This creates a new method, does NOT override parent's private
private void privateMethod() {
System.out.println("Derived private");
}
}| Can it be overridden? | Reason |
|---|---|
final method | No — intentionally locked by parent |
static method | No — static methods are hidden, not overridden; runtime polymorphism does not apply |
private method | No — not visible to child at all |
public or protected method | Yes |
Overriding vs Overloading — Side by Side
| Feature | Overriding | Overloading |
|---|---|---|
| Class | Parent + child | Same class (or parent/child) |
| Method name | Same | Same |
| Parameters | Same | Different |
| Return type | Same or covariant | Can be different |
| Resolved | Runtime | Compile time |
| Polymorphism type | Runtime (dynamic) | Compile time (static) |
@Override | Should use | Cannot use |
Common Mistake 1: Narrowing Access Modifier
public class Parent {
public void show() { System.out.println("Parent"); }
}
public class Child extends Parent {
// COMPILE ERROR — cannot reduce from public to protected
@Override
protected void show() { System.out.println("Child"); }
}Overriding can only make access wider (protected → public) or keep it the same. Never narrower.
Common Mistake 2: Trying to Override static or final
public class Parent {
static void greet() { System.out.println("Hello"); }
}
public class Child extends Parent {
@Override
static void greet() { System.out.println("Hi"); } // COMPILE ERROR
}Static methods belong to the class, not objects. Runtime dispatch does not apply to them. Calling Parent.greet() always calls Parent's version; calling Child.greet() always calls Child's version — this is hiding, not overriding.
Common Mistake 3: Missing @Override and Creating an Overload Instead
public class Animal {
void eat(String food) { System.out.println("Eating " + food); }
}
public class Cat extends Animal {
// Intended override, but wrong parameter type — actually an overload
void eat(int amount) { System.out.println("Eating " + amount + " grams"); }
}Cat.eat(int) does not override Animal.eat(String) — they have different parameters. Without @Override, the compiler will not warn you. Add @Override and the compile error will immediately show the mistake.
Practice Task
Task: Create a class Shape with a method area() that returns 0.0. Create two subclasses:
Circlewith a fieldradius, overridesarea()to return3.14159 * radius * radiusRectanglewith fieldslengthandwidth, overridesarea()to returnlength * width
In main, create a Shape[] with one Circle (radius 5) and one Rectangle (4 × 6). Loop through and print each area.
Hint: Use @Override on both. Store both in a Shape[] — this is upcasting. The loop calls each object's own area() at runtime.
Solution:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
What to observe: The loop uses Shape reference. s.area() calls each object's own overridden version. Add a Triangle class with its own area() — the loop handles it with zero changes to the loop itself.
Continue the lesson path