Start learning free

LESSON 183 OF 344 · OBJECT-ORIENTED PROGRAMMING

Method Overriding in Java: @Override and Runtime Behavior

Lesson Read the full explanation

TL ; DR:

When a child class defines a method with the same name as the parent's, the child's version overrides the parent's. Different objects can give the same method name different behavior.


class Dog {
    public void bark() { System.out.println("Woof!"); }
}
class Puppy extends Dog {
    @Override
    public void bark() { System.out.println("Yip!"); }
}


Full lesson:

You've seen that a child class inherits the parent's methods. Overriding is what happens when the child wants to change one of those inherited methods.

Why override?

Subclasses are usually special cases of the parent. A Puppy is a Dog, but it sounds different — "Yip!" instead of "Woof!". Instead of inventing a new method name, you redefine bark() on the Puppy. Callers don't need to know which subclass they're holding — they just call bark() and the right version runs.

How it works in each language

  • C# — the parent method must be marked virtual, the child marks its replacement override. The compiler enforces this pairing.
  • Java — every non-final method is virtual by default; the @Override annotation is optional but tells the compiler to verify the signature matches.
  • Python — defining a method with the same name in the child class replaces the parent's. No keyword required.
  • JavaScript — same as Python: define the method again on the child class and it wins.
  • C++ — the parent method must be virtual; override on the child is optional but compiler-checked.

Your task

The Dog class already defines bark. Inside the Puppy subclass, override bark so it prints "Yip! I am " followed by the puppy's name. The harness creates a Puppy named "Buddy" and calls bark() on it.

Expected output:

Yip! I am Buddy
STEP 1 OF 2
    Ctrl + Enter to check

    JAVA · OBJECT-ORIENTED PROGRAMMING

    Method Overriding in Java, explained

    What overriding means

    A child class inherits its parent's methods. Overriding is what happens when the child wants to change one of them: it declares a method with the same name and parameters as the parent's, and the child's version is used for child objects.

    Subclasses are special cases of the parent. A Puppy is a Dog, but it sounds different. Rather than inventing a new method name, you redefine bark() in Puppy. Code that calls bark() doesn't need to know which kind of dog it holds.

    The syntax

    class Dog {
        public void bark() { System.out.println("Woof!"); }
    }
    
    class Puppy extends Dog {
        @Override
        public void bark() { System.out.println("Yip!"); }
    }
    

    Java decides which version to run by the object's real class. Given a Puppy object, bark() prints Yip!. Given a plain Dog, it prints Woof!. Java needs no special keyword on the parent: every method is overridable by default unless it is final, static or private.

    The @Override annotation

    @Override is optional, but you should always write it. It tells the compiler "this method is meant to replace one from a parent", and the compiler then checks that a matching parent method exists. Without it, a small slip creates a brand new method and no error appears. With it, the slip is caught right away:

    class Puppy extends Dog {
        @Override
        public void bark(int times) { }
    }
    

    The compiler rejects this with method does not override or implement a method from a supertype. The parent's method is bark() with no parameters, and bark(int times) has a different signature, so it would be a separate overload and not an override. A misspelling such as barc() triggers the same error.

    The rules for a valid override

    • The name and the parameter types must match the parent's method exactly.
    • The return type must be the same, or a subtype of it for object types.
    • The access level cannot be narrower. A parent's public method cannot be overridden as private or package-private.
    • Methods marked final cannot be overridden at all.

    Reaching the parent's version

    An override can still use the code it replaced. Writing super.bark() inside the child runs the parent's version on the same object:

    class Puppy extends Dog {
        public Puppy(String name) { super(name); }
    
        @Override
        public void bark() {
            super.bark();
            System.out.println("(squeaky)");
        }
    }
    

    For a puppy named Buddy, this prints Woof! I am Buddy followed by (squeaky). That keeps the parent's behavior and adds to it, without copying its code.

    The lesson's exercise, step by step

    The starter code gives Dog a bark that prints Woof! I am and the name, and gives Puppy only a constructor. Two steps change the puppy's sound:

    1. Declare the override. Inside Puppy, add @Override and public void bark(), with the same name, no parameters and the same void return type as the parent.
    2. Fill in the body. Print "Yip! I am " followed by name. The field is inherited from Dog, and it is public, so Puppy can use it by its bare name.

    The complete program:

    class Dog {
        public String name;
        public Dog(String name) { this.name = name; }
        public void bark() {
            System.out.println("Woof! I am " + name);
        }
    }
    
    class Puppy extends Dog {
        public Puppy(String name) { super(name); }
        @Override
        public void bark() {
            System.out.println("Yip! I am " + name);
        }
    }
    
    class Solution {
        public void mainFunction() {
            Puppy buddy = new Puppy("Buddy");
            buddy.bark();
        }
    }
    

    The output is:

    Yip! I am Buddy
    

    Because the variable holds a Puppy, Java runs the version in Puppy. Dogs that are not puppies still print Woof! I am ....

    The chosen version depends on the object

    The version of a method that runs depends on the object, not on the variable's declared type. If you assign a puppy to a Dog variable, the puppy still barks like a puppy:

    Dog d = new Puppy("Buddy");
    d.bark();
    

    This prints Yip! I am Buddy. The next lesson, on polymorphism, makes use of exactly this behavior.

    Overriding and overloading

    The two words sound alike but mean different things. Overriding replaces an inherited method in a subclass, using the same signature. Overloading puts several methods with the same name but different parameter lists in the same class. @Override is correct only for the first.

    Common mistakes

    • Changing the parameter list. Adding or changing a parameter creates an overload, not an override.
    • Leaving out @Override. The code compiles even when you misspell the method name, and the parent's version silently keeps running.
    • Weakening the access. Writing void bark() with no modifier in the child, when the parent's is public, is a compile error.
    • Declaring a second name field in the child. Both versions of bark read the same inherited field. A new field in Puppy would hide it, and the constructor's super(name) would no longer fill the one the child uses.
    • Expecting static methods to be overridden. Static methods are hidden, not overridden, and they don't follow the object's real class.

    Key takeaways

    • A child overrides a parent method by declaring one with the same name and parameters.
    • Mark overrides with @Override, so the compiler catches signature mistakes.
    • Java picks the version using the object's actual class at runtime.
    • super.method() calls the parent's version from inside an override.
    • The access level of an override may stay the same or become wider, but never narrower.
    Keep going with JavaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free