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:
Let me show you a complete, working example so you understand exactly what happens:
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:
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.
Consider the following code. Which statement is correct?
- 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.
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.
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.
(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.
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.
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.
(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.
(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.
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).
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).
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).
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.
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:
Output:
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.
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.
Consider the following class. Which statement is correct?
- 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.
Given the class below, what is the output of new Test().display(10, 20)?
- 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!
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.
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:
I need you to look at this line very carefully:
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.
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.
- 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.
What is the output of the following code?
- 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.
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.
Consider the following code. What happens?
- 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).
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:
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.
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.
| Feature | Static Polymorphism | Dynamic Polymorphism |
|---|---|---|
| When resolved | Compile time | Runtime |
| Also called | Early binding, static binding | Late binding, dynamic binding |
| Achieved by | Method overloading | Method overriding |
| Who decides | Compiler | JVM |
| Speed | Faster (decided early) | Slightly slower (decided at runtime) |
| Flexibility | Less flexible | More flexible |
| Static methods | Can participate | Cannot participate |
What is the output of the following code?
- 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.
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
Output:
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
Output:
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.
- 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).
What is the output of the following code?
- 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.
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.
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
Full Working Example from Your Textbook
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.
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.
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.
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.
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);
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.
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
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:
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?
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.
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.
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.
| Feature | Abstract Class | Interface |
|---|---|---|
| Keyword | abstract class | interface |
| Methods | Abstract + concrete methods | Only abstract methods (pre-Java 8) |
| Variables | Any type (instance variables) | Only public static final (constants) |
| Constructor | Can have constructors | Cannot have constructors |
| Inheritance | extends (single class only) | implements (multiple allowed) |
| Access modifiers | Can use any modifier | Methods: public abstract (auto) |
| Multiple inheritance | NOT supported | Supported |
| IS-A relationship | Yes | Yes |
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.
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.
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.
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()
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.
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.
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.
What is the result of the following code?
- 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 method: Cannot be overridden by any subclass.
Final class: Cannot be extended by any subclass.
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
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
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!
What is the output?
- 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”.
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.
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.
What is the output of the following code?
- 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.
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.
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.
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.
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
- 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.
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!