Inheritance and Polymorphism in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)

Hello there, my dear students! I am really glad you are here today. Chapter 3 is one of those chapters that can make or break your exam score. But do not worry at all. I will walk you through every single concept step by step, just like we do in our classroom. Have your notebook ready, and let us get started!

Before we begin, let me ask you a quick question. Why do you think object-oriented programming became so popular? Think about it for a second. Okay, here is the answer: because it lets us reuse code that we already wrote. Instead of building everything from zero every time, we build on top of what exists. And the mechanism that makes this possible is called inheritance. Let us understand it deeply.

1. What Is Inheritance?

Imagine you are a teacher. You prepare detailed lecture notes for Chapter 3. Now your colleague, who teaches the same course at another campus, needs notes for the same chapter. Would they write everything from scratch? Of course not! They would simply take your notes and add their own examples or explanations on top. That is exactly how inheritance works in programming.

In simple words, inheritance is a mechanism where a new class acquires all the properties and behaviors of an existing class. The class that already exists is called the superclass (also known as base class or parent class). The new class that gets all those properties is called the subclass (also known as derived class or child class).

Now, why is this so useful? Let me give you a real-world analogy. Think about vehicles. All vehicles share some common features — they have a color, a speed, and they can move. A Car is a vehicle. A Truck is a vehicle. A Motorcycle is a vehicle. Instead of defining color, speed, and move() separately for Car, Truck, and Motorcycle, we create one Vehicle class with those features, and then each specific type simply inherits from Vehicle. Brilliant, right?

In Java, all classes are arranged in a hierarchy. At the very top of this hierarchy is the Object class. Every single class in Java, whether you write it yourself or use a built-in one, is a subclass of Object. So inheritance is everywhere in Java, even if you do not see it directly.

How to Declare Inheritance in Java

Java uses the reserved word extends to create an inheritance relationship between two classes. The syntax is straightforward:

class SubclassName extends SuperclassName { // additional fields and methods }

Let me show you a complete, working example so you understand exactly what happens:

class Vehicle { String color; int speed;void move() { System.out.println(“Vehicle is moving at speed ” + speed); } }class Car extends Vehicle { int numberOfDoors;void honk() { System.out.println(“Beep beep!”); } }public class InheritanceDemo { public static void main(String[] args) { Car myCar = new Car(); myCar.color = “Red”; // inherited from Vehicle myCar.speed = 120; // inherited from Vehicle myCar.numberOfDoors = 4; // own field of Car myCar.move(); // inherited method from Vehicle myCar.honk(); // own method of Car } }

Look at that carefully. Inside the Car class, we never wrote the fields color and speed. We never wrote the move() method. But we can still use them! Why? Because Car extends Vehicle, and Vehicle already has those members. The Car class inherited them automatically. This is code reuse in action, and it saves you from writing redundant code.

UML Class Diagram Representation

In UML diagrams, inheritance is shown with an arrow that has an open arrowhead pointing from the child class to the parent class. This arrow means “extends” or “is a type of.” Here is a simple diagram:

+————-+ +————-+ | Vehicle |<——–| Car | +————-+ +————-+ | – color | | – doors | | – speed | +————-+ +————-+ | + honk() | | + move() | +————-+ +————-+Arrow points FROM child TO parent. Read as: “Car IS-A Vehicle”
Critical Rule: The IS-A Relationship

Inheritance must always represent an IS-A relationship. This means you should be able to say “Subclass IS-A Superclass” and it should make logical sense. Car IS-A Vehicle — correct. Dog IS-A Animal — correct. But Student IS-A Classroom — wrong! A student is not a classroom. If IS-A does not make sense, do not use inheritance. Use something else like composition (HAS-A relationship) instead.

Exam Practice – MCQ 1

Consider the following code. Which statement is correct?

class A { int x = 10; void show() { System.out.println(x); } } class B extends A { int y = 20; } public class Test { public static void main(String[] args) { B obj = new B(); obj.show(); } }
  • Compilation error because show() is not defined in B
  • Compilation error because x is not accessible in B
  • Output: 10
  • Output: 20

Answer: C. Output: 10

Explanation: Class B extends A, which means B inherits both the field x and the method show(). When we create a B object and call obj.show(), the JVM finds show() in the parent class A and executes it. Inside show(), x refers to A’s x which is 10. There is no error because inherited members are fully accessible in the subclass. Option A is wrong because inherited methods are available. Option B is wrong because inherited fields are accessible (x is not private). Option D is wrong because show() prints x, not y.

Exam Practice – MCQ 2

Which of the following correctly demonstrates an IS-A relationship in Java?

  • class Engine { } class Car { Engine e; }
  • class Student { } class Classroom { Student s; }
  • class Person { } class Employee extends Person { }
  • class Book { } class Library { Book b; }

Answer: C. class Person { } class Employee extends Person { }

Explanation: The IS-A relationship is established through inheritance using the extends keyword. An Employee IS-A Person — this makes logical sense. Options A, B, and D all show a HAS-A relationship (composition), where one class contains an object of another class. A Car HAS-A Engine, a Classroom HAS-A Student, a Library HAS-A Book. These are not IS-A relationships and do not use the extends keyword.

Fill in the Blanks

1. The mechanism of deriving a new class from an existing class is called inheritance.

2. The existing class that is being extended is called the superclass or parent class.

3. In Java, the keyword used to create inheritance is extends.

4. In a UML diagram, the inheritance arrow points from the child class to the parent class.

5. Every Java class is ultimately a subclass of the Object class.

1. inheritance — The fundamental OOP mechanism for code reuse through class hierarchy.

2. superclass or parent class — Also called base class. The class whose members are inherited.

3. extends — This is the only keyword in Java for class-to-class inheritance.

4. child class to parent class — The arrow always points toward the more general class.

5. Object — The root of the entire Java class hierarchy, found in java.lang package.

2. Types of Inheritance

Now here is something you must be very careful about. There are several types of inheritance in theory, but Java does not support all of them. Let me go through each type one by one with clear diagrams, and I will tell you exactly which ones Java supports and which ones it rejects. Pay close attention because this is a very common exam area.

(a) Single Inheritance

This is the simplest form. One subclass extends one superclass. Just one parent, one child. Nothing complicated here.

+——-+ | Class A| +——-+ | | extends | +——-+ | Class B| +——-+One superclass, one subclass. Fully supported by Java.
class Animal { void eat() { System.out.println(“Eating”); } } class Dog extends Animal { void bark() { System.out.println(“Barking”); } }

(b) Multiple Inheritance

In multiple inheritance, one child class tries to extend two or more parent classes at the same time. Now, this sounds useful in theory, but it creates a serious problem called the diamond problem. Here is what happens: if both parent classes have a method with the same name, and the child inherits from both, which version should the child use? There is no way to decide, and this creates ambiguity.

Because of this problem, Java does NOT support multiple inheritance through classes. This is a very important fact. Write it down and circle it.

+——-+ +——-+ | Class B| | Class C| +——-+ +——-+ | | | extends | +——-+——-+ | +——-+ | Class A| +——-+Class A tries to extend both B and C. JAVA REJECTS THIS through classes! Reason: Diamond problem (ambiguity)
Why Java Says No to Multiple Inheritance

Suppose Class B has a method display() that prints “Hello” and Class C has a method display() that prints “World”. If Class A extends both B and C, and you call a.display(), Java cannot decide whether to run B’s version or C’s version. This confusion is called ambiguity. Java’s designers decided to completely avoid this problem by disallowing multiple inheritance through classes. However, you CAN achieve the effect of multiple inheritance using interfaces (we will learn about that later in this chapter).

(c) Hierarchical Inheritance

In hierarchical inheritance, one superclass has multiple subclasses. Many children, one parent. Think of it like a tree with one root and many branches. This is fully supported by Java and is very commonly used.

+——-+ | Class A| +——-+ / | \ / | \ / | \ +——-+ +——-+ +——-+ | Class B| | Class C| | Class D| +——-+ +——-+ +——-+One superclass, many subclasses. Fully supported by Java. Example: Animal → Dog, Cat, Bird
class Animal { void eat() { System.out.println(“Eating”); } } class Dog extends Animal { void bark() { System.out.println(“Barking”); } } class Cat extends Animal { void meow() { System.out.println(“Meowing”); } } class Bird extends Animal { void fly() { System.out.println(“Flying”); } }

Notice how Dog, Cat, and Bird all share the eat() method from Animal, but each one adds its own unique behavior. That is the beauty of hierarchical inheritance — shared code at the top, specific code at the bottom.

(d) Multi-Level Inheritance

Here we have a chain of inheritance. A class extends a class that already extends another class. It is like a family tree: grandparent → parent → child. Each level adds more specific features.

+——-+ | Class A| +——-+ | | extends | +——-+ | Class B| +——-+ | | extends | +——-+ | Class C| +——-+Chain: A → B → C Fully supported by Java. Example: Animal → Mammal → Dog
class Animal { void eat() { System.out.println(“Eating”); } } class Mammal extends Animal { void breathe() { System.out.println(“Breathing with lungs”); } } class Dog extends Mammal { void bark() { System.out.println(“Barking”); } }// A Dog object can use eat(), breathe(), AND bark() // because it inherits from the entire chain

(e) Hybrid Inheritance

Hybrid inheritance is a combination of two or more types of inheritance. For example, you might have hierarchical inheritance at one level and multi-level inheritance at another level, all mixed together. Since hybrid inheritance can involve multiple inheritance scenarios, Java supports it only when interfaces are used for the parts that would otherwise require multiple class inheritance.

+——-+ | Class A| +——-+ / \ / \ +——-+ +——-+ | Class B| | Class C| +——-+ +——-+ | | extends | +——-+ | Class D| +——-+Mix of Hierarchical (A→B, A→C) and Multi-Level (A→B→D) = Hybrid Inheritance

(f) Multi-Path Inheritance

In multi-path inheritance, a class inherits from two different paths that lead back to a common ancestor. This is essentially the diamond problem in a different shape. The child class receives properties from two sources that share a common base. Java does NOT support this through classes, but it can be achieved using interfaces.

+——-+ | Class A| +——-+ / \ / \ +——-+ +——-+ | Class B| | Class C| +——-+ +——-+ \ / \ / +——-+ | Class D| +——-+D inherits from both B and C, both of which inherit from A. Diamond shape! Not supported through classes.
Exam Tip – Types of Inheritance (Memorize This Table)

Supported through classes: Single, Hierarchical, Multi-Level

NOT supported through classes: Multiple, Multi-Path

Supported through interfaces only: Multiple, Multi-Path, Hybrid

If the exam asks “Which type is NOT supported by Java?” the most likely answer is Multiple Inheritance or Multi-Path Inheritance (through classes).

Exam Practice – MCQ 3

In a class hierarchy where Account is the superclass, SavingsAccount extends Account, and FixedDepositAccount extends SavingsAccount, which type of inheritance is demonstrated?

  • Single inheritance
  • Hierarchical inheritance
  • Multi-level inheritance
  • Hybrid inheritance

Answer: C. Multi-level inheritance

Explanation: The chain Account → SavingsAccount → FixedDepositAccount has three levels, where each class extends the one above it. This is the definition of multi-level inheritance. There is only one subclass per superclass at each level, so it is not hierarchical. There is no combination of types, so it is not hybrid. Single inheritance would mean only two levels (one parent, one child).

Exam Practice – MCQ 4

Why does Java prohibit multiple inheritance through classes?

  • It makes the code run slower
  • It causes ambiguity when two superclasses have methods with the same signature
  • It violates the IS-A relationship principle
  • Java does not have a mechanism to call superclass constructors

Answer: B. It causes ambiguity when two superclasses have methods with the same signature

Explanation: The core reason is the ambiguity problem, also known as the diamond problem. If two parent classes define a method with the same name and parameters, the compiler cannot determine which version the child should inherit. This is not about performance (A is wrong). Multiple inheritance can actually follow IS-A in some cases (C is wrong). Java does have super() to call parent constructors (D is wrong).

Exam Practice – MCQ 5

Given the classes: class A { }, class B extends A { }, class C extends A { }. Which type of inheritance does this represent?

  • Single inheritance
  • Multi-level inheritance
  • Hierarchical inheritance
  • Multiple inheritance

Answer: C. Hierarchical inheritance

Explanation: Class A is the single superclass, and both B and C extend A. This is one parent with multiple children — the exact definition of hierarchical inheritance. Single inheritance would mean only B extends A (just one child). Multi-level would mean B extends A and C extends B (a chain). Multiple inheritance would mean B extends both A and some other class simultaneously.

3. Benefits of Inheritance

Students often memorize the benefits without understanding them. Let me explain each one so you truly get it and can write about it in the exam with confidence.

1. Code Reusability: This is the number one benefit. When a subclass inherits from a superclass, it automatically gets all the fields and methods of the parent. You do not rewrite them. Imagine you have a Person class with name, age, and address fields. Now if you create Student, Teacher, and Employee classes, all of them need name, age, and address. Instead of writing these three fields three times, you write them once in Person and inherit them. That is reuse, and it saves a massive amount of time and effort.

2. Code Maintenance: When your code is organized in a hierarchy, maintaining it becomes much easier. Suppose you discover a bug in the eat() method of the Animal class. You fix it once in Animal, and all subclasses (Dog, Cat, Bird, etc.) automatically get the fix. You do not need to go to each subclass and fix the same bug repeatedly. This centralized maintenance is a huge advantage.

3. Implementing OOP Philosophy: The real world is hierarchical. Living things are organized as Animal → Mammal → Dog. Vehicles are organized as Vehicle → Car → Sedan. Inheritance lets your program structure mirror this natural hierarchy. This makes your code more intuitive and closer to how humans actually think about the world.

4. Increased Reliability: When you inherit from a superclass, you are reusing code that has already been tested and proven to work. Tested code is reliable code. Instead of writing new, untested code for common features, you rely on the battle-tested parent class code. This directly leads to fewer bugs and more robust software.

Quick Memory Formula for Benefits
$$\text{Benefits of Inheritance} = \text{Reusability} + \text{Maintenance} + \text{OOP Philosophy} + \text{Reliability}$$ $$\text{Remember: “R-M-O-R” (Reusability, Maintenance, OOP, Reliability)}$$
Exam Practice – MCQ 6

Which benefit of inheritance is demonstrated when a bug fix in the superclass automatically resolves the issue in all its subclasses?

  • Code reusability
  • Code maintenance
  • Increased reliability
  • Implementing OOP philosophy

Answer: B. Code maintenance

Explanation: Code maintenance refers to the ease of managing and updating code. When a fix in the parent class propagates to all children automatically, that is maintenance in action. Code reusability (A) is about not rewriting code in the first place. Reliability (C) is about using tested code. OOP philosophy (D) is about mirroring real-world hierarchies. The scenario described specifically matches maintenance.

4. Method Overloading (Compile-Time Polymorphism)

Have you ever used System.out.println() to print an integer, then used the same println() to print a string, and then used it again to print a double? The method name is the same every time — println(). But it handles different types of data. How? This is possible because println() is overloaded.

Method overloading means having two or more methods in the same class with the same name but different parameter declarations. The methods perform similar tasks but accept different types or different numbers of input values.

There are exactly two ways to overload a method in Java:

Way 1: Change the number of arguments — one version takes 2 parameters, another takes 3.

Way 2: Change the data type of arguments — one version takes int, another takes double.

Let me show you a clear example that demonstrates both ways:

class Sum { // Version 1: two int arguments void add(int a, int b) { System.out.println(“Sum of two ints = ” + (a + b)); }
See also  Classes and Objects in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)
// Version 2: three double arguments // (different number AND different data type) void add(double a, double b, double c) { System.out.println(“Sum of three doubles = ” + (a + b + c)); }// Version 3: one int and one double // (different data type combination) void add(int a, double b) { System.out.println(“Sum of int + double = ” + (a + b)); } }public class OverLoadDemo { public static void main(String[] args) { Sum s = new Sum(); s.add(20, 25); // calls Version 1 s.add(10.1, 12.8, 7.1); // calls Version 2 s.add(5, 3.5); // calls Version 3 } }

Output:

Sum of two ints = 45 Sum of three doubles = 30.0 Sum of int + double = 8.5

The Java compiler looks at the arguments you pass and matches them with the correct version of add(). This matching happens at compile time, which is why method overloading is called compile-time polymorphism or static polymorphism.

Exam Tip – What Counts as “Different” in Overloading?

These changes make overloaded methods valid (they change the parameter list):

  • Number of parameters: add(int a) vs add(int a, int b)
  • Data types: add(int a, int b) vs add(double a, double b)
  • Order of data types: add(int a, double b) vs add(double a, int b)

These do NOT make overloaded methods valid:

  • Changing only the return type (same parameters, different return type = ERROR)
  • Changing only the access modifier
  • Changing only parameter variable names

The compiler uses the method signature (method name + parameter list) to distinguish overloaded methods. Return type is NOT part of the signature.

Mathematical View of Method Signatures
$$\text{Method Signature} = \text{methodName} + \text{parameterTypes} + \text{parameterCount} + \text{parameterOrder}$$ $$\text{Return type} \notin \text{Signature} \implies \text{Cannot overload by return type alone}$$
Exam Practice – MCQ 7

Consider the following class. Which statement is correct?

class Calc { public int compute(int a, int b) { return a + b; } public double compute(int a, int b) { return a + b + 0.0; } }
  • This is valid method overloading
  • Compilation error: duplicate method
  • Compilation error: return type cannot be double
  • Runtime error when compute is called

Answer: B. Compilation error: duplicate method

Explanation: Both methods have the exact same name (compute) and the exact same parameter list (int a, int b). Even though the return types are different (int vs double), this does NOT count as overloading because the return type is not part of the method signature. The compiler sees two methods with identical signatures and produces a “duplicate method” error. Remember: you cannot overload by changing only the return type.

Exam Practice – MCQ 8

Given the class below, what is the output of new Test().display(10, 20)?

class Test { void display(int a, long b) { System.out.println(“int-long”); } void display(long a, int b) { System.out.println(“long-int”); } }
  • int-long
  • long-int
  • Compilation error: ambiguous method call
  • Runtime error

Answer: C. Compilation error: ambiguous method call

Explanation: When you call display(10, 20), both 10 and 20 are int literals. Now, the compiler tries to find the best match. Version 1 (int, long) requires widening the second argument from int to long. Version 2 (long, int) requires widening the first argument from int to long. Both conversions are equally valid, and neither is more specific than the other. The compiler cannot decide which version to call, so it produces an “ambiguous method call” error. This is a classic tricky question!

Write / Workout

Create a class called AreaCalculator with three overloaded methods named calculateArea():
1. One that takes one double parameter (radius) and returns the area of a circle.
2. One that takes two double parameters (length, width) and returns the area of a rectangle.
3. One that takes three double parameters (base, height, and a dummy flag of type int) and returns the area of a triangle.
Write a main method that calls all three versions and prints the results. Explain why this is overloading and not overriding.

class AreaCalculator { double calculateArea(double radius) { return 3.14159 * radius * radius; }double calculateArea(double length, double width) { return length * width; }double calculateArea(double base, double height, int flag) { return 0.5 * base * height; } }public class TestArea { public static void main(String[] args) { AreaCalculator ac = new AreaCalculator(); System.out.println(“Circle: ” + ac.calculateArea(5.0)); System.out.println(“Rectangle: ” + ac.calculateArea(4.0, 6.0)); System.out.println(“Triangle: ” + ac.calculateArea(4.0, 5.0, 1)); } }

Explanation: This is overloading because all three methods are in the same class, have the same name (calculateArea), but have different parameter lists (1 parameter vs 2 parameters vs 3 parameters). The third version uses an int flag parameter to differentiate from the two-double version (since Java does not consider return type for overloading). Overriding, on the other hand, requires a parent-child class relationship and identical parameter lists. Since there is no inheritance here, it cannot be overriding.

5. Method Overriding (Runtime Polymorphism)

Now here is a question for you. What if the child class inherits a method from the parent, but the child wants to do things differently? The child cannot change the parent’s code. So what does it do? It overrides the method.

Method overriding means a subclass provides its own implementation of a method that is already defined in its superclass. Both methods have the same name and the same parameter list, but the body (implementation) is different. The subclass says, “I know my parent does it this way, but I want to do it my way.”

The big benefit of overriding is that a subclass can define a behavior specific to its own type. A Dog moves differently from a generic Animal. A SavingsAccount calculates interest differently from a generic Account. Overriding lets each type be itself.

Let me show you the classic example:

class Animal { void move() { System.out.println(“Animals can move”); } }class Dog extends Animal { void move() { System.out.println(“Dogs can walk and run”); } }public class OverRideDemo { public static void main(String[] args) { Animal a = new Animal(); // Animal reference, Animal object Animal b = new Dog(); // Animal reference, Dog objecta.move(); // Output: Animals can move b.move(); // Output: Dogs can walk and run } }

I need you to look at this line very carefully:

Animal b = new Dog();

This line creates a Dog object but stores it in an Animal reference variable. When we call b.move(), you might think it would call Animal’s move() because the reference type is Animal. But it does NOT. It calls Dog’s move(). Why? Because at runtime, the JVM looks at the actual object type (Dog), not the reference type (Animal). The actual object is a Dog, so Dog’s version of move() runs. This is called dynamic method dispatch.

Crucial Difference: Overloading vs Overriding

Overloading: Same class, same method name, DIFFERENT parameters. Resolved at compile time. Also called static polymorphism.

Overriding: Parent-child classes, same method name, SAME parameters, different body. Resolved at runtime. Also called dynamic polymorphism.

If you mix up these two on the exam, you will lose easy marks. So please memorize the difference clearly.

Exam Tip – Rules of Method Overriding (Must Know All Five)
  • Rule 1: The method name and parameter list must be exactly the same in both parent and child.
  • Rule 2: The return type must be the same or a covariant type (a subclass of the original return type).
  • Rule 3: A static method CANNOT be overridden. If the child defines the same static method, it is called method hiding, not overriding.
  • Rule 4: A final method CANNOT be overridden. The final keyword prevents modification.
  • Rule 5: The access modifier in the child cannot be more restrictive than in the parent. If parent uses protected, child can use protected or public, but NOT private.
Exam Practice – MCQ 9

What is the output of the following code?

class Animal { void move() { System.out.println(“Animals can move”); } } class Dog extends Animal { void move() { System.out.println(“Dogs can walk and run”); } } class Puppy extends Dog { void move() { System.out.println(“Puppies can crawl”); } } public class Test { public static void main(String[] args) { Animal a = new Puppy(); a.move(); } }
  • Animals can move
  • Dogs can walk and run
  • Puppies can crawl
  • Compilation error

Answer: C. Puppies can crawl

Explanation: This is multi-level inheritance combined with overriding. The chain is Animal → Dog → Puppy. The reference type is Animal, but the actual object created is Puppy. At runtime, the JVM looks at the actual object (Puppy) and finds that Puppy has its own version of move(). So it calls Puppy’s move(), printing “Puppies can crawl”. The JVM always calls the most specific overridden version available for the actual object type.

Exam Practice – MCQ 10

Which of the following statements about method overriding is FALSE?

  • The overriding method must have the same name and parameter list as the overridden method
  • A static method in the parent class can be overridden in the child class
  • A final method in the parent class cannot be overridden in the child class
  • The access modifier of the overriding method cannot be more restrictive than the overridden method

Answer: B. A static method in the parent class can be overridden in the child class

Explanation: This statement is FALSE. Static methods belong to the class, not to objects. When a child class defines a static method with the same signature as a parent’s static method, it is called method hiding, NOT method overriding. There is no runtime polymorphism for static methods. Options A, C, and D are all correct statements about overriding rules.

Exam Practice – MCQ 11

Consider the following code. What happens?

class Parent { protected void display() { System.out.println(“Parent”); } } class Child extends Parent { private void display() { System.out.println(“Child”); } }
  • Child’s display() overrides Parent’s display() successfully
  • Compilation error: attempting to assign weaker access privileges
  • Runtime error when display() is called
  • Both methods coexist and can be called depending on the reference type

Answer: B. Compilation error: attempting to assign weaker access privileges

Explanation: The parent’s display() is protected, which allows access within the same package and in subclasses. The child’s display() is private, which is more restrictive than protected. Rule 5 of overriding states that the child’s access modifier cannot be more restrictive. Since private is more restrictive than protected, the compiler rejects this code with the error message shown in the answer. The child could use protected or public, but not private or default (if they are in different packages).

Fill in the Blanks – Overriding

1. Method overriding allows a subclass to provide its own implementation of a parent class method.

2. In overriding, the method call is resolved at runtime by the JVM.

3. A final method cannot be overridden in Java.

4. When a parent reference points to a child object and calls an overridden method, the child class version executes.

5. The process where the JVM determines the method to call based on the actual object type is called dynamic method dispatch.

1. implementation — The child writes its own method body.

2. runtime — This is why overriding is runtime polymorphism.

3. final — Final means “no changes allowed,” so overriding is blocked.

4. child class — Dynamic dispatch checks the actual object, not the reference.

5. dynamic method dispatch — The technical term for this runtime resolution mechanism.

6. Polymorphism in Depth

The word Polymorphism comes from Greek: Poly means “many” and Morph means “forms.” So polymorphism literally means “many forms.” In programming, it means the same thing can behave differently in different situations. A single method name can do different things depending on the context.

A polymorphic reference is a variable that can refer to different types of objects at different points in time during program execution. For example, an Animal reference can point to a Dog object now and a Cat object later. The same reference, different forms — that is polymorphism.

There are exactly two types of polymorphism in Java, and you must know both deeply:

6.1 Static Polymorphism (Compile-Time Polymorphism)

In static polymorphism, the compiler knows at compile time exactly which method will be called. There is no waiting until runtime. The compiler examines the method signature (name and parameter types) and makes the decision right then and there. This is also called static binding or early binding.

Method overloading is the primary example of static polymorphism. When you call an overloaded method, the compiler matches the arguments with the parameter list and selects the correct version at compile time.

Here is an important example from your textbook that uses static methods:

class Animal { static void move() { System.out.println(“Animals can move”); } }class Dog extends Animal { static void move() { System.out.println(“Dogs can walk and run”); } }public class StaticPolyDemo { public static void main(String[] args) { Animal.move(); // Output: Animals can move Dog.move(); // Output: Dogs can walk and run } }

Notice that both methods are static. Static methods are called using the class name, and the compiler resolves the call immediately. There is no runtime decision. Also, even though Dog has a static move() with the same signature as Animal’s static move(), this is NOT overriding. This is method hiding. The Dog class hides Animal’s static move(), it does not override it.

6.2 Dynamic Polymorphism (Runtime Polymorphism)

In dynamic polymorphism, the compiler does NOT know at compile time which method will be called. The decision is postponed until runtime, when the JVM examines the actual object type and calls the appropriate method. This is also called dynamic binding or late binding.

Method overriding is the primary example of dynamic polymorphism. When a parent class reference points to a child class object, the JVM discovers the actual object type at runtime and calls the overridden method of that specific child class.

class Animal { void move() { System.out.println(“Animals can move”); } }class Dog extends Animal { void move() { System.out.println(“Dogs can walk and run”); } }public class DynamicPolyDemo { public static void main(String[] args) { Animal a = new Animal(); Animal b = new Dog(); // Polymorphic reference!a.move(); // Output: Animals can move b.move(); // Output: Dogs can walk and run } }

The magic happens at Animal b = new Dog(). At compile time, the compiler only knows that b is of type Animal. It checks that Animal has a move() method (it does) and says “okay, this is valid.” But at runtime, the JVM sees that the actual object is a Dog, and it calls Dog’s move(). The compiler’s decision is overridden by the JVM’s runtime decision. That is dynamic polymorphism.

Side-by-Side Comparison Table
FeatureStatic PolymorphismDynamic Polymorphism
When resolvedCompile timeRuntime
Also calledEarly binding, static bindingLate binding, dynamic binding
Achieved byMethod overloadingMethod overriding
Who decidesCompilerJVM
SpeedFaster (decided early)Slightly slower (decided at runtime)
FlexibilityLess flexibleMore flexible
Static methodsCan participateCannot participate
Mathematical Representation
$$\text{Static Poly: } f_{\text{call}} = \text{Compiler}\big(\text{method\_signature}\big) \implies \text{Resolved at } t = t_{\text{compile}}$$ $$\text{Dynamic Poly: } f_{\text{call}} = \text{JVM}\big(\text{actual\_object\_type}\big) \implies \text{Resolved at } t = t_{\text{runtime}}$$
Exam Practice – MCQ 12

What is the output of the following code?

class Shape { void draw() { System.out.println(“Shape”); } } class Circle extends Shape { void draw() { System.out.println(“Circle”); } } class Square extends Shape { void draw() { System.out.println(“Square”); } } public class Test { public static void main(String[] args) { Shape s; s = new Circle(); s.draw(); s = new Square(); s.draw(); } }
  • Shape / Shape
  • Circle / Square
  • Shape / Circle
  • Compilation error

Answer: B. Circle / Square

Explanation: This is dynamic polymorphism in action. The reference s is of type Shape. First, it points to a Circle object — s.draw() calls Circle’s draw(), printing “Circle”. Then s is reassigned to point to a Square object — s.draw() now calls Square’s draw(), printing “Square”. The same reference s takes “many forms” — first a Circle, then a Square. The JVM decides which draw() to call based on the actual object type at runtime.

Exam Practice – MCQ 13

In the context of polymorphism, what does “dynamic binding” mean?

  • The compiler links the method call to the method body during compilation
  • The JVM links the method call to the method body during execution
  • The method call is linked to a static method
  • The method body is generated dynamically at runtime

Answer: B. The JVM links the method call to the method body during execution

Explanation: “Dynamic binding” (also called late binding) means the connection between a method call and the actual method code is made at runtime by the JVM, not at compile time by the compiler. This happens in method overriding scenarios where the actual object type determines which method runs. Option A describes static binding (early binding). Option C is incorrect because static methods use static binding, not dynamic binding. Option D is wrong because Java does not generate method bodies at runtime.

7. The Super Keyword

Sometimes when you override a method, you still want to use the parent’s version inside the child’s version. You do not want to completely replace the parent’s behavior — you want to extend it. How do you call the parent’s method from inside the child? You use the super keyword.

The super keyword has exactly two uses in Java:

Use 1: To call the superclass constructor. When a child object is created, the parent’s constructor should run first to initialize the inherited fields. You call the parent’s constructor using super().

Use 2: To access superclass members. When the child class has a member with the same name as the parent’s member, you can use super.memberName to specifically access the parent’s version.

Use 1: Calling Superclass Constructor

// Syntax: super(); // calls the no-argument constructor of parent super(arg1, arg2); // calls the parameterized constructor of parent
class Animal { String name;
See also  Exception Handling in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)
Animal(String name) { this.name = name; System.out.println(“Animal constructor: ” + name); } }class Dog extends Animal { String breed;Dog(String name, String breed) { super(name); // MUST be the very first line! this.breed = breed; System.out.println(“Dog constructor: ” + breed); } }public class TestSuper { public static void main(String[] args) { Dog d = new Dog(“Buddy”, “Labrador”); } }

Output:

Animal constructor: Buddy Dog constructor: Labrador

Notice that super(name) is the very first line inside the Dog constructor. This is not optional — it is a strict Java rule. The parent constructor must complete before the child constructor runs. If you do not write super() explicitly, Java automatically inserts a call to the no-argument super() as the first line. But if the parent does not have a no-argument constructor, you MUST explicitly call super() with the right arguments, or you get a compilation error.

Use 2: Accessing Superclass Methods

// Syntax: super.methodName(); // calls the parent’s version of the method super.variableName; // accesses the parent’s version of the variable
class Animal { public void move() { System.out.println(“Animals can move”); } }class Dog extends Animal { public void move() { super.move(); // calls Animal’s move() first System.out.println(“Dogs can walk and run”); } }public class TestDog { public static void main(String[] args) { Animal b = new Dog(); b.move(); } }

Output:

Animals can move Dogs can walk and run

Do you see what happened? Inside Dog’s move(), we first called super.move() to execute the parent’s behavior, and then we added Dog’s own behavior. The output shows both messages. This pattern is very common in real programming — you extend the parent’s behavior rather than completely replacing it.

Strict Rules for Super Keyword
  • super() must be the first statement in the constructor. No exceptions.
  • If you do not write super(), Java inserts super() (no-arg version) automatically.
  • super can access both methods and instance variables of the parent class.
  • super cannot be used in a static context (static methods have no object, so no parent object to refer to).
Exam Practice – MCQ 14

What is the output of the following code?

class Parent { Parent() { System.out.println(“Parent no-arg”); } Parent(int x) { System.out.println(“Parent param: ” + x); } } class Child extends Parent { Child() { super(10); System.out.println(“Child no-arg”); } } public class Test { public static void main(String[] args) { Child c = new Child(); } }
  • Parent no-arg / Child no-arg
  • Parent param: 10 / Child no-arg
  • Child no-arg / Parent param: 10
  • Compilation error

Answer: B. Parent param: 10 / Child no-arg

Explanation: When new Child() is called, the Child constructor executes. Its first line is super(10), which calls Parent’s parameterized constructor with argument 10, printing “Parent param: 10”. After the parent constructor finishes, the rest of the Child constructor runs, printing “Child no-arg”. The parent constructor always executes before the child constructor body. Option C is wrong because the parent always runs first.

Exam Practice – MCQ 15

What happens if you place super() as the second statement in a constructor?

  • It still works correctly
  • Compilation error
  • The parent constructor runs after the child constructor
  • The parent constructor is not called at all

Answer: B. Compilation error

Explanation: Java requires that super() (or this()) must be the very first statement in a constructor. If it appears as the second or third line, the compiler immediately produces an error. This rule exists to guarantee that the parent class is fully initialized before the child class begins its own initialization. There are no exceptions to this rule.

Fill in the Blanks – Super Keyword

1. The super keyword is used to refer to the immediate parent class.

2. To call the parent’s parameterized constructor, use super(arguments).

3. The super() call must be the first statement in the constructor.

4. To call the parent’s overridden method from inside the child, use super.methodName().

5. The super keyword cannot be used inside a static method.

1. super — The keyword that provides access to the immediate parent.

2. super(arguments) — Pass the correct number and type of arguments.

3. first — Absolute rule, no exceptions allowed by the compiler.

4. super.methodName() — Use the dot operator to call a specific parent method.

5. cannot — Static context has no object, so super makes no sense there.

8. Abstract Classes and Abstract Methods

Let me ask you a question. Can you draw a generic “Shape” on paper? Not a circle, not a rectangle — just a “Shape.” You cannot, right? Because “Shape” is too abstract. But you CAN draw a circle or a rectangle, which are specific shapes. Java has a way to represent this idea: the abstract class.

An abstract class is a class that is declared with the abstract keyword. It may contain:

  • Abstract methods — methods with no body (just the signature, ending with a semicolon)
  • Concrete methods — regular methods with a full body
  • Constructors and instance variables — yes, abstract classes CAN have these!

The idea is simple: the abstract class defines what is common (concrete methods and fields) and what must be done by each subclass (abstract methods). The abstract method says, “Every subclass must do this, but I don’t know HOW — each subclass will figure that out.”

Complete Rules for Abstract Classes

Rule 1: Declared with the abstract keyword.

Rule 2: You CANNOT create an object of an abstract class. Why? Because it may have abstract methods with no body — incomplete code. Java will not let you instantiate something incomplete.

Rule 3: To use an abstract class, you must create a subclass that extends it and implements ALL abstract methods.

Rule 4: An abstract method has no body — it ends with a semicolon, not curly braces.

Rule 5: If a subclass does NOT implement all abstract methods, it must also be declared as abstract.

Rule 6: A class CANNOT be both abstract and final. This is a direct contradiction — abstract means “must be extended” and final means “cannot be extended.”

Rule 7: An abstract class reference can point to a subclass object (polymorphism works).

Rule 8: An abstract class reference cannot directly call methods that exist only in the subclass (not defined in the abstract class).

Syntax

// Abstract class syntax abstract class ClassName { // concrete methods (with body) // abstract methods (without body) // constructors // instance variables }// Abstract method syntax (no body, just semicolon) public abstract void methodName(); public abstract void methodName(parameterList);

Full Working Example from Your Textbook

abstract class Figure { double dim1; double dim2;// Abstract class CAN have a constructor! Figure(double a, double b) { dim1 = a; dim2 = b; }// Abstract method: no body abstract double area(); }class Rectangle extends Figure { Rectangle(double a, double b) { super(a, b); // calls Figure’s constructor }// MUST implement the abstract method double area() { System.out.println(“Inside Area of Rectangle.”); return dim1 * dim2; } }class Triangle extends Figure { Triangle(double a, double b) { super(a, b); }// MUST implement the abstract method double area() { System.out.println(“Inside Area of Triangle.”); return dim1 * dim2 / 2; } }public class AbstractAreas { public static void main(String[] args) { // Figure f = new Figure(5, 10); // ERROR! Cannot create object of abstract classRectangle r = new Rectangle(9, 5); Triangle t = new Triangle(10, 8);System.out.println(“Area is ” + r.area()); // 45.0 System.out.println(“Area is ” + t.area()); // 40.0 } }

Let me walk you through this carefully. The Figure class says: “I know every figure has two dimensions (dim1, dim2), but I don’t know how to calculate the area because I don’t know what shape I am.” So area() is abstract. Rectangle implements area() as dim1 * dim2 (length times width). Triangle implements area() as dim1 * dim2 / 2 (half of base times height for a right triangle). Each subclass provides its own specific implementation.

Also notice: Figure has a constructor that initializes dim1 and dim2. Both Rectangle and Triangle call super(a, b) to use this constructor. The abstract class constructor runs when a subclass object is created, even though you cannot create a Figure object directly.

Common Exam Traps – Do Not Fall for These!

Trap 1: “Can an abstract class have a constructor?” — YES! The constructor runs when a subclass object is created via super().

Trap 2: “Can an abstract class have zero abstract methods?” — YES! A class can be abstract with no abstract methods. This is done to prevent direct instantiation.

Trap 3: “Can you write: final abstract class A?” — NO! Compilation error. They contradict each other.

Trap 4: “Can an abstract class reference call subclass-specific methods?” — NO! It can only call methods defined in the abstract class itself.

Exam Practice – MCQ 16

Which of the following statements about abstract classes is TRUE?

  • An abstract class can be instantiated using the new keyword
  • An abstract class can have constructors
  • An abstract method must have a body enclosed in curly braces
  • A class can be declared with both abstract and final keywords

Answer: B. An abstract class can have constructors

Explanation: Abstract classes can have constructors, which are called when a subclass object is created (via super()). Option A is false — you cannot instantiate an abstract class. Option C is false — abstract methods have NO body (they end with a semicolon). Option D is false — abstract and final are contradictory and produce a compilation error.

Exam Practice – MCQ 17

What happens when a concrete class extends an abstract class but does not implement one of its abstract methods?

  • The unimplemented method gets an empty body automatically
  • Compilation error
  • Runtime exception
  • The class is automatically promoted to abstract

Answer: B. Compilation error

Explanation: A concrete (non-abstract) class MUST implement ALL abstract methods inherited from its parent. If it fails to implement even one, the compiler produces an error. Java does not silently provide empty bodies (A is wrong). It does not automatically make the class abstract (D is wrong) — you would need to explicitly add the abstract keyword yourself. There is no runtime issue because the code never compiles.

Write / Workout

Create an abstract class BankAccount with:
– A field: balance (double), initialized to 10000
– A constructor that sets the balance
– An abstract method: double calculateInterest()
– A concrete method: void deposit(double amount) that prints “Deposited: amount”

Create two subclasses:
SavingsAccount: calculateInterest() returns balance * 0.05
FixedDeposit: calculateInterest() returns balance * 0.08

Write a main method that creates objects of both types and calls both methods on each. Explain why we cannot write: BankAccount b = new BankAccount(10000);

abstract class BankAccount { double balance;BankAccount(double balance) { this.balance = balance; }abstract double calculateInterest();void deposit(double amount) { System.out.println(“Deposited: ” + amount); } }class SavingsAccount extends BankAccount { SavingsAccount(double balance) { super(balance); }double calculateInterest() { return balance * 0.05; } }class FixedDeposit extends BankAccount { FixedDeposit(double balance) { super(balance); }double calculateInterest() { return balance * 0.08; } }public class TestBank { public static void main(String[] args) { // BankAccount b = new BankAccount(10000); // ERROR! Cannot instantiate abstract classSavingsAccount sa = new SavingsAccount(10000); FixedDeposit fd = new FixedDeposit(10000);sa.deposit(500); System.out.println(“Savings Interest: ” + sa.calculateInterest());fd.deposit(1000); System.out.println(“FD Interest: ” + fd.calculateInterest()); } }

Explanation: We cannot write new BankAccount(10000) because BankAccount is abstract — it has an abstract method (calculateInterest) with no body. Creating an object would mean having an incomplete method, which Java prevents. Instead, we create concrete subclass objects (SavingsAccount and FixedDeposit) that provide the complete implementation. The deposit() method is shared by all subclasses (defined in the abstract class), while calculateInterest() is specific to each subclass (overridden from the abstract method). This is a practical, real-world use of abstract classes.

9. Interface in Java

Now, here is a problem. What if you want a class to inherit from TWO parents? We already know Java says NO to multiple inheritance through classes. So what is the solution? The answer is Interface.

An interface in Java is like a blueprint of a class. Think of it as a contract. The interface says, “If you want to be called [something], you must be able to do these things.” But the interface does not tell you HOW to do them — only WHAT you must be able to do. The class that signs the contract (implements the interface) provides the actual implementation.

For example, a Printable interface says: “If you want to be printable, you must have a print() method.” But it does not say what print() should print. Each class that implements Printable decides what to print.

Key Characteristics of Interfaces

1. An interface has only abstract methods (before Java 8) and static constants.

2. You cannot instantiate an interface — just like an abstract class.

3. A class uses the implements keyword to use an interface (NOT extends).

4. Interface methods are public and abstract by default — the compiler adds these keywords for you.

5. Interface fields are public, static, and final by default — they are constants.

6. A class can implement multiple interfaces — this is Java’s way of achieving multiple inheritance!

7. An interface can extend another interface (interfaces use extends with each other).

8. An interface represents an IS-A relationship — just like a class.

Remember the Keyword Rules

Class extends Class — regular inheritance between two classes

Class implements Interface — a class fulfills an interface contract

Interface extends Interface — one interface inherits from another

A class NEVER “extends” an interface. An interface NEVER “implements” anything. Get this right and you will avoid many exam mistakes.

Simple Interface Example

interface Printable { void print(); // automatically public and abstract }class Printed implements Printable { public void print() { System.out.println(“Hello”); }public static void main(String[] args) { Printed p = new Printed(); p.print(); // Output: Hello } }

The Printable interface has one abstract method: print(). The Printed class implements Printable and provides the body for print(). When we create a Printed object and call print(), it executes the implementation we wrote. Simple and clean.

Multiple Inheritance Through Interfaces

This is one of the most important concepts in this chapter. Let me show you how a class can implement multiple interfaces at the same time:

interface Printable { void print(); }interface Showable { void show(); }class Demo implements Printable, Showable { public void print() { System.out.println(“Hello”); }public void show() { System.out.println(“Welcome”); }public static void main(String[] args) { Demo d = new Demo(); d.print(); // Output: Hello d.show(); // Output: Welcome } }

The class Demo implements both Printable and Showable. This is perfectly valid in Java. The class must provide implementations for ALL methods from ALL interfaces. This gives you the power of multiple inheritance without the diamond problem.

What If Two Interfaces Have the Same Method?

This is a great exam question. Suppose two interfaces both declare a method with the same signature. What happens?

interface Printable { void print(); }interface Showable { void print(); // same method name and parameters }class Demo implements Printable, Showable { // Only ONE implementation needed! public void print() { System.out.println(“Hello”); }public static void main(String[] args) { Demo d = new Demo(); d.print(); // Output: Hello } }

Even though both interfaces have print(), the class only needs to provide one implementation. There is no ambiguity because interfaces have NO method bodies — they only declare the method signature. The class provides the single body, and that single body satisfies both interfaces. This is why multiple inheritance through interfaces is safe.

Why Multiple Inheritance Through Interfaces Has No Ambiguity

With classes: Two parent classes might have different implementations of the same method → ambiguity (which body to use?).

With interfaces: Two interfaces have the same method signature but NO body → no conflict. The implementing class provides one body that works for both.

$$\text{Class multiple inheritance: } \text{body}_1 \neq \text{body}_2 \implies \text{Ambiguity}$$ $$\text{Interface multiple inheritance: } \text{no body in interfaces} \implies \text{Class provides } 1 \text{ body} \implies \text{No ambiguity}$$

Three Reasons to Use Interfaces

1. To achieve full abstraction: Interfaces contain only abstract methods (by default), meaning they represent 100% abstraction. There is no concrete implementation inside an interface (before Java 8). This is “purer” abstraction than abstract classes, which can have concrete methods.

2. To support multiple inheritance: Since Java does not allow multiple inheritance through classes, interfaces provide the only way for a class to inherit behavior from multiple sources. A class can implement any number of interfaces.

3. To achieve loose coupling: When two classes interact through an interface rather than directly, they are loosely coupled. You can replace one class with another as long as both implement the same interface. This makes your system more flexible and easier to modify.

Exam Tip – Abstract Class vs Interface (High-Yield Comparison)
FeatureAbstract ClassInterface
Keywordabstract classinterface
MethodsAbstract + concrete methodsOnly abstract methods (pre-Java 8)
VariablesAny type (instance variables)Only public static final (constants)
ConstructorCan have constructorsCannot have constructors
Inheritanceextends (single class only)implements (multiple allowed)
Access modifiersCan use any modifierMethods: public abstract (auto)
Multiple inheritanceNOT supportedSupported
IS-A relationshipYesYes
Exam Practice – MCQ 18

Which keyword is used by a class to inherit from an interface in Java?

  • extends
  • implements
  • interface
  • abstract

Answer: B. implements

Explanation: In Java, a class uses implements to inherit from an interface. The extends keyword is for class-to-class inheritance or interface-to-interface inheritance. The interface keyword is for declaring an interface. The abstract keyword is for declaring abstract classes or methods.

Exam Practice – MCQ 19

By default, the data members (fields) in a Java interface are:

  • public and abstract
  • public, static, and final
  • protected and static
  • private and final

Answer: B. public, static, and final

Explanation: The Java compiler automatically adds public, static, and final to every field in an interface. This means interface fields are constants — they belong to the interface (static), are accessible everywhere (public), and cannot be changed (final). Option A describes interface methods, not fields. Options C and D use incorrect access modifiers for interface members.

See also  Packages in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)
Exam Practice – MCQ 20

A class implements two interfaces. Both interfaces declare void display(). How many implementations of display() must the class provide?

  • Two separate implementations
  • One implementation that satisfies both
  • Zero — the compiler provides a default
  • Compilation error due to ambiguity

Answer: B. One implementation that satisfies both

Explanation: When two interfaces declare the same method signature, the implementing class provides only one implementation. Since interfaces have no method bodies, there is no conflict to resolve. The single implementation satisfies the contract of both interfaces. This is different from class-based multiple inheritance, where two parent classes might have conflicting implementations. Option D is wrong precisely because interfaces have no bodies to conflict.

Write / Workout

Create two interfaces: Drawable (method: void draw()) and Resizable (method: void resize(double factor)). Create a class SmartCircle that implements both. In draw(), print “Drawing a circle”. In resize(), print “Circle resized by factor: ” + factor. Write a main method that:
1. Creates a SmartCircle object
2. Calls both methods directly
3. Also demonstrates polymorphism by storing SmartCircle in a Drawable reference and calling draw()
4. Also stores SmartCircle in a Resizable reference and calls resize()

interface Drawable { void draw(); }interface Resizable { void resize(double factor); }class SmartCircle implements Drawable, Resizable { public void draw() { System.out.println(“Drawing a circle”); }public void resize(double factor) { System.out.println(“Circle resized by factor: ” + factor); } }public class TestSmartCircle { public static void main(String[] args) { // Direct calls SmartCircle sc = new SmartCircle(); sc.draw(); // Drawing a circle sc.resize(2.5); // Circle resized by factor: 2.5// Polymorphism with Drawable Drawable d = new SmartCircle(); d.draw(); // Drawing a circle// Polymorphism with Resizable Resizable r = new SmartCircle(); r.resize(1.5); // Circle resized by factor: 1.5 } }

Explanation: SmartCircle implements both interfaces, providing concrete methods for draw() and resize(). The polymorphism part shows that an interface reference can point to an implementing class object — just like a parent class reference can point to a child object. Drawable reference d can only call draw() (not resize()), and Resizable reference r can only call resize() (not draw()). This demonstrates both multiple inheritance through interfaces and polymorphism with interfaces.

10. Additional Important Topics (From Assignment)

Your chapter assignment mentions several extra topics. Let me cover each one in detail because they often appear in exams.

10.1 Type Casting

Casting means converting a reference from one type to another. In inheritance, there are two directions:

Upcasting: Converting a child reference to a parent type. This is always safe and happens automatically (implicitly). You are going “up” the hierarchy toward the more general type.

Dog d = new Dog(); Animal a = d; // Upcasting (implicit, always safe) // Or directly: Animal a2 = new Dog(); // Upcasting

After upcasting, the reference can only access methods defined in the parent class. Child-specific methods are not accessible through the parent reference.

Downcasting: Converting a parent reference to a child type. This is NOT always safe and requires an explicit cast. It is only safe if the actual object IS of the child type.

Animal a = new Dog(); Dog d = (Dog) a; // Safe! Actual object IS a DogAnimal a2 = new Animal(); Dog d2 = (Dog) a2; // UNSAFE! Runtime: ClassCastException
Exam Tip – Casting

Upcasting = safe, implicit, child → parent. Loses access to child-specific methods.

Downcasting = potentially unsafe, explicit cast required, parent → child. Only safe if actual object matches the target type. Otherwise: ClassCastException.

Exam Practice – MCQ 21

What is the result of the following code?

class Animal { void eat() {} } class Dog extends Animal { void bark() {} } public class Test { public static void main(String[] args) { Animal a = new Dog(); Dog d = (Dog) a; d.bark(); } }
  • Compilation error
  • ClassCastException at runtime
  • Runs successfully, calls bark()
  • Runs but does nothing

Answer: C. Runs successfully, calls bark()

Explanation: The actual object is a Dog. Downcasting the Animal reference to Dog using (Dog) a is safe because the actual object IS a Dog. After the cast, d is a Dog reference pointing to a Dog object, so d.bark() works perfectly. The key is that the actual object type must match the cast type.

10.2 Final Keyword

The final keyword means “cannot be changed” or “cannot be modified.” It has three uses:

Final variable: Once assigned, its value cannot be changed. It becomes a constant.

final double PI = 3.14159; PI = 3.14; // ERROR! Cannot change a final variable

Final method: Cannot be overridden by any subclass.

class Parent { final void display() { System.out.println(“Hello”); } } class Child extends Parent { void display() { } // ERROR! Cannot override final method }

Final class: Cannot be extended by any subclass.

final class MathUtil { } class AdvancedMath extends MathUtil { } // ERROR!
Exam Practice – MCQ 22

Which of the following is TRUE about the final keyword?

  • A final class can be extended
  • A final method can be overridden
  • A final variable can be reassigned once
  • A final class cannot be extended

Answer: D. A final class cannot be extended

Explanation: When a class is declared final, no class can extend it. Options A, B, and C are all false. A final class cannot be extended (A is wrong). A final method cannot be overridden (B is wrong). A final variable cannot be reassigned even once (C is wrong — it can only be assigned once, at declaration or in a constructor).

10.3 Object Class

The Object class (in java.lang) is the superclass of all Java classes. Every class directly or indirectly extends Object. Even when you write “class Dog { }”, Java treats it as “class Dog extends Object { }”.

Every class inherits these methods from Object:

  • toString() — returns a string representation of the object
  • equals(Object obj) — compares two objects for equality
  • hashCode() — returns a hash code value for the object
  • getClass() — returns the runtime class of the object
  • clone() — creates and returns a copy of the object
  • finalize() — called by the garbage collector before the object is destroyed
+————-+ | Object | +————-+ | + toString() | | + equals() | | + hashCode() | | + getClass() | +————-+ | | (every class extends Object) | +——–+——–+——–+ | | | | +——-+ +——-+ +——+ +——–+ |Animal | |Vehicle| |String| |YourClass| +——-+ +——-+ +——+ +——–+The Object class is at the TOP of every hierarchy.
Exam Practice – MCQ 23

Which class is at the root of the Java class hierarchy?

  • Class
  • Super
  • Object
  • Main

Answer: C. Object

Explanation: The Object class (java.lang.Object) is the root of the entire Java class hierarchy. Every class in Java inherits from Object either directly (if no extends is specified) or indirectly (through its superclass chain). There is no class above Object — Object itself has no superclass. This means every Java object can use toString(), equals(), hashCode(), etc., because they are inherited from Object.

10.4 Hierarchical Inheritance – Detailed Example

class Employee { String name; int id;Employee(String name, int id) { this.name = name; this.id = id; }void display() { System.out.println(“Name: ” + name + “, ID: ” + id); } }class Manager extends Employee { String department;Manager(String name, int id, String department) { super(name, id); this.department = department; }void display() { super.display(); System.out.println(“Department: ” + department); } }class Developer extends Employee { String language;Developer(String name, int id, String language) { super(name, id); this.language = language; }void display() { super.display(); System.out.println(“Language: ” + language); } }

In this example, both Manager and Developer extend the same Employee class. Each overrides display() to add its own information while still using the parent’s display() via super.display(). This is hierarchical inheritance with method overriding.

10.5 Hybrid and Multi-Path Inheritance

Hybrid inheritance combines two or more types. In Java, this is typically achieved by mixing class inheritance with interface implementation. For example, a class extends one class and implements multiple interfaces.

Multi-path inheritance involves a class receiving properties from two paths sharing a common ancestor. In Java, this is resolved through interfaces since interfaces avoid the ambiguity problem.

11. Final Comprehensive Quiz

Okay, my dear students! We have covered everything in this chapter. Now it is time to test yourself. Try these questions honestly before looking at the answers. Good luck!

Final Quiz – MCQ 24

What is the output?

class A { void show() { System.out.println(“A”); } } class B extends A { void show() { System.out.println(“B”); } } class C extends B { void show() { System.out.println(“C”); } } public class Test { public static void main(String[] args) { A obj = new C(); obj.show(); } }
  • A
  • B
  • C
  • Compilation error

Answer: C. C

Explanation: Multi-level inheritance (A → B → C) with overriding at each level. The reference type is A, but the actual object is C. Dynamic method dispatch finds the most specific overridden version — C’s show(). Output is “C”.

Final Quiz – MCQ 25

Which statement about interfaces is FALSE?

  • An interface can extend another interface
  • An interface can have constructors
  • An interface can have abstract methods
  • A class can implement multiple interfaces

Answer: B. An interface can have constructors

Explanation: Interfaces cannot have constructors. Constructors initialize object state, but interfaces cannot be instantiated and have no instance state (only constants). Options A, C, and D are all true.

Final Quiz – MCQ 26

Given: abstract class Shape { abstract void draw(); } Which is a valid subclass?

  • class Circle extends Shape { }
  • class Circle extends Shape { void draw() { } }
  • abstract class Circle extends Shape { }
  • Both B and C are valid

Answer: D. Both B and C are valid

Explanation: Option B is valid because Circle provides a concrete implementation of draw(). Option C is valid because an abstract subclass is not required to implement the inherited abstract methods — it can pass the responsibility to its own subclasses. Option A is invalid because a concrete class cannot leave abstract methods unimplemented.

Final Quiz – MCQ 27

What is the output of the following code?

class Base { Base() { System.out.print(“Base “); } } class Mid extends Base { Mid() { System.out.print(“Mid “); } } class Der extends Mid { Der() { System.out.print(“Der “); } } public class Test { public static void main(String[] args) { Der d = new Der(); } }
  • Der
  • Base Mid Der
  • Der Mid Base
  • Mid Base Der

Answer: B. Base Mid Der

Explanation: In a multi-level inheritance chain, constructors are called from the top of the hierarchy downward. When new Der() is called: first Base() runs (printing “Base “), then Mid() runs (printing “Mid “), then Der() runs (printing “Der “). This is because each constructor implicitly calls super() as its first statement, going all the way up to Base before coming back down.

Final Quiz – MCQ 28

Which of the following best describes why Java uses interfaces to achieve multiple inheritance?

  • Interfaces run faster than classes
  • Interfaces provide default implementations that resolve ambiguity
  • Interfaces have no method bodies, so there is no ambiguity about which implementation to use
  • Interfaces use a different memory model that prevents conflicts

Answer: C. Interfaces have no method bodies, so there is no ambiguity about which implementation to use

Explanation: The diamond problem in class-based multiple inheritance occurs because two parent classes might have different method bodies for the same method signature. Interfaces avoid this because they only declare method signatures without providing bodies. The implementing class supplies the body, so there is always exactly one implementation — no conflict, no ambiguity. Option B is wrong because traditional interfaces (before Java 8) do NOT provide default implementations.

Final Quiz – Fill in the Blanks (Comprehensive)

1. The process of deriving a new class from an existing class is called inheritance.

2. Method overloading is an example of compile-time polymorphism.

3. Method overriding is an example of runtime polymorphism.

4. An abstract method is declared with the abstract keyword and has no body.

5. A class uses the implements keyword to use an interface.

6. The Object class is the superclass of all Java classes.

7. A final class cannot be extended by any subclass.

8. In an interface, all fields are public static final by default.

9. Converting a parent reference to a child type is called downcasting.

10. Multiple inheritance through classes is not supported in Java.

11. The super keyword refers to the immediate parent class.

12. An interface cannot have constructors.

1. inheritance — The core OOP mechanism for deriving classes from existing ones.

2. Method overloading — Same name, different parameters, compile-time resolution.

3. Method overriding — Same name, same parameters, runtime resolution.

4. abstract — The declaration keyword; no body — ends with semicolon.

5. implements — The keyword for class-to-interface relationship.

6. Object — The universal root class in Java (java.lang.Object).

7. final — Prevents any class from extending it.

8. public static final — Interface fields are constants with these auto-applied modifiers.

9. downcasting — Requires explicit cast; safe only if actual object matches.

10. Multiple — Due to the diamond problem; achievable through interfaces instead.

11. super — Used for calling parent constructors and accessing parent members.

12. cannot — Interfaces have no instance state to initialize.

Final Comprehensive Workout

Create a Java application with the following complete structure:

1. An abstract class Person with fields name and age, a constructor, an abstract method String getRole(), and a concrete method void displayInfo() that prints name, age, and role.

2. Two concrete subclasses: Student (getRole returns “Student”) and Teacher (getRole returns “Teacher”).

3. An interface ExamTaker with method void takeExam(). Only Student implements this interface.

4. In main:
  – Create a Person reference pointing to a Student, call displayInfo()
  – Create a Person reference pointing to a Teacher, call displayInfo()
  – Create an ExamTaker reference pointing to a Student, call takeExam()

5. Explain every output line and identify which concepts (inheritance, overriding, polymorphism, interface, upcasting) are demonstrated where.

abstract class Person { String name; int age;Person(String name, int age) { this.name = name; this.age = age; }abstract String getRole();void displayInfo() { System.out.println(“Name: ” + name + “, Age: ” + age + “, Role: ” + getRole()); } }interface ExamTaker { void takeExam(); }class Student extends Person implements ExamTaker { String studentId;Student(String name, int age, String studentId) { super(name, age); this.studentId = studentId; }String getRole() { return “Student”; }public void takeExam() { System.out.println(name + ” is taking the exam. ID: ” + studentId); } }class Teacher extends Person { String subject;Teacher(String name, int age, String subject) { super(name, age); this.subject = subject; }String getRole() { return “Teacher”; } }public class TestApplication { public static void main(String[] args) { // Inheritance + Overriding + Dynamic Polymorphism Person p1 = new Student(“Alice”, 20, “STU001”); Person p2 = new Teacher(“Bob”, 35, “Java”);p1.displayInfo(); // Name: Alice, Age: 20, Role: Student p2.displayInfo(); // Name: Bob, Age: 35, Role: Teacher// Interface + Upcasting + Polymorphism ExamTaker et = new Student(“Charlie”, 21, “STU002”); et.takeExam(); // Charlie is taking the exam. ID: STU002 } }

Concept-by-concept explanation:

  • Abstract class (Person): Has constructor, concrete method (displayInfo), and abstract method (getRole). Cannot be instantiated directly.
  • Inheritance: Student and Teacher extend Person, inheriting name, age, and displayInfo().
  • Method overriding: Both Student and Teacher override getRole() with their own implementation.
  • Super keyword: Student and Teacher constructors call super(name, age) to initialize Person’s fields.
  • Dynamic polymorphism: Person p1 points to a Student object. When p1.displayInfo() calls getRole() inside it, the JVM dispatches to Student’s getRole() at runtime. Same for p2 → Teacher’s getRole().
  • Interface (ExamTaker): Only Student implements it, showing that not all subclasses need the same interface.
  • Upcasting: Student object is assigned to ExamTaker reference (implicit, safe upcast).
  • Reference type limitation: p1.takeExam() would NOT compile because Person does not define takeExam(), even though the actual object is a Student.

12. Quick Revision – Everything at a Glance

Master Formula Sheet for Your Exam
$$\textbf{Inheritance:}\quad \text{class Child extends Parent}$$ $$\textbf{Interface:}\quad \text{class Child implements Interface}$$ $$\textbf{Interface extends Interface:}\quad \text{interface B extends A}$$ $$\textbf{Overloading:}\quad \text{Same name} + \text{Different params} \implies \text{Compile time}$$ $$\textbf{Overriding:}\quad \text{Same name} + \text{Same params} + \text{Different body} \implies \text{Runtime}$$ $$\textbf{Super constructor:}\quad \text{super(args)} \implies \text{Must be first line}$$ $$\textbf{Super method:}\quad \text{super.methodName()}$$ $$\textbf{Abstract class:}\quad \text{abstract class Name }\{\text{ abstract void m(); }\}$$ $$\textbf{Interface methods:}\quad \text{public abstract (auto-applied)}$$ $$\textbf{Interface fields:}\quad \text{public static final (auto-applied)}$$ $$\textbf{Contradiction:}\quad \text{final abstract class A} \implies \text{ERROR}$$ $$\textbf{Root class:}\quad \text{All classes extend Object (implicit)}$$ $$\textbf{Dynamic dispatch:}\quad f_{\text{called}} = f(\text{actual object type at runtime})$$
Final Exam Strategy
  • Polymorphism questions: Always look at the actual object type, not the reference type. The object type decides which method runs at runtime.
  • Overloading vs overriding: Check if methods are in the same class (overloading) or parent-child (overriding). Check if parameters are different (overloading) or same (overriding).
  • Abstract class questions: Remember: no object creation, subclass must implement abstract methods, can have constructors, cannot be final.
  • Interface questions: Remember: implements (not extends) for classes, methods are public abstract, fields are public static final, no constructors.
  • Super questions: super() must be first line, super() calls parent constructor, super.method() calls parent method.
  • Inheritance type questions: Multiple and multi-path are NOT supported through classes. Hybrid requires interfaces.
  • Casting questions: Upcasting = safe + implicit. Downcasting = explicit cast + only safe if actual object matches.
Congratulations, Students!

You have now completed a thorough study of Chapter 3: Inheritance and Polymorphism. You learned about inheritance, all six types, method overloading, method overriding, static and dynamic polymorphism, the super keyword, abstract classes, interfaces, type casting, the final keyword, and the Object class. These are the pillars of OOP in Java.

Remember: understanding is not enough — you must practice. Write code for every concept. Create your own examples. Solve more problems. The more you practice, the more confident you will feel when you see that exam paper.

You have worked hard, and I believe in you. Best of luck on your exam!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top