🌳
OOP

Inheritance

Parent → Child
💡 In inheritance, a child class "inherits" features from its parent class — like a son inheriting his father's traits, plus adding his own new ones.

The extends keyword lets one class use another class's fields and methods, without rewriting them.

This boosts code reuse — write the common stuff in the parent, the specific stuff in the child.

class Animal {
  void eat() { System.out.println("Eating..."); }
}
class Dog extends Animal {
  void bark() { System.out.println("Woof!"); }
}
🌳
In inheritance, a child class "inherits" features from its parent class — like a son inheriting his father's traits, plus adding his own new ones.
1 / 4
⚡ Quick Recap
  • extends gives you the parent's features
  • Boosts code reuse
  • A child can add its own new things too
On this page (4 subtopics)

Single: a class extends just one parent (Dog extends Animal) — the most common type.

Multilevel: forms a chain (Puppy extends Dog extends Animal) — Puppy gets Dog's features and Animal's too (transitively).

Hierarchical: one parent has multiple children (Dog and Cat both extend Animal) — both get Animal's common behavior, but also have their own specific behavior.

Multiple inheritance (a class extending two classes, like class C extends A, B) is NOT allowed for Java classes — to avoid the "diamond problem" (if A and B both have a method with the same name, C wouldn't know which one to use). This is partially achieved through interfaces (a class can implement multiple interfaces).

TypeExample
SingleDog extends Animal
MultilevelPuppy extends Dog extends Animal
HierarchicalDog extends Animal, Cat extends Animal
Multiple (classes)NOT allowed in Java

super.method() calls the parent class's overridden method (from inside the child) — useful when the child wants to *extend* the parent's behavior, not replace it. super.field accesses the parent's field, if the child has a field with the same name (variable "shadowing"). super(...) calls the parent constructor.

class Dog extends Animal {
  void makeSound() {
    super.makeSound(); // pehle parent ka version chalao
    System.out.println("Woof!"); // fir apna add karo
  }
}

For overriding, the method signature (name + parameters, same order/types) must match exactly between parent and child. The return type can be the same, or "covariant" (a subtype).

Adding @Override is best practice — the compiler checks that you're truly overriding something (a typo would give a compile error, which is far better than a silent bug).

The access modifier in the child can't be more restrictive than the parent's — you can't turn a public method into protected while overriding it (this is the "visibility can widen, not narrow" rule).

⚠️Common Mistake: Without @Override, if you typo a method name (like forgetting makeSound() and writing makeSond() instead), Java will think it's a brand NEW method, not an error — the bug hides silently. Adding @Override lets the compiler catch it immediately.

When a child class's object is created, the parent class's constructor runs first (implicitly, or explicitly via super()), then the child's constructor. This guarantees the parent's setup is complete before the child builds on top of it.

This chain goes all the way to the root — with 3-level inheritance (A -> B -> C), the order when an object is created is: A's constructor, then B's, then C's.