In this tutorial, we have compiled the top 10 Java inheritance programs for the best practice. Each program is explained step by step in a clear, beginner-friendly way so you can easily understand how inheritance works in Java.
These hands-on examples are ideal for coding practice, college exams, viva voce, and technical interview preparation. Practicing them will help you master key concepts of inheritance.
If you are new to OOP concepts or want to refresh your knowledge, we recommend reading our detailed tutorial on inheritance in Java first. It covers the core theory from scratch, making these practice programs much easier to follow.
Single-Level Inheritance Program in Java
Write a Java program to demonstrate single-level inheritance where a subclass inherits a non-private instance method from its parent class and defines its own method.
Example Program 1: Single-Level Inheritance
// Base class (Superclass)
class A {
// Accessible instance method
public void methodA() {
System.out.println("Base class method");
}
}
// Derived class (Subclass) extending class A
class B extends A {
// Child class specific method
public void methodB() {
System.out.println("Child class method");
}
}
// Main driver class (saved as MyClass.java)
public class MyClass {
public static void main(String[] args) {
// Instantiate the derived class B
B obj = new B();
// Calling the inherited method from class A
obj.methodA();
// Calling the child class's own method
obj.methodB();
}
}
Expected Output:
Base class method Child class method
In this example program:
- The declaration class B extends A, establishing an IS-A relationship where A is the parent class and B is the child class.
- Since methodA() has public access in class A, it is inherited by class B without needing to be re-declared.
- Calling obj.methodA() on the B reference invokes the inherited method from class A, while obj.methodB() invokes the method defined directly within class B.
- Only MyClass is declared public so the entire snippet can be compiled and executed directly from a single source file named MyClass.java.
Multilevel Inheritance Program in Java
Write a Java program to demonstrate multilevel inheritance where a subclass inherits members transitively through an intermediate parent class.
Example Program 2: Multilevel Inheritance
// Grandparent class
class X {
public void methodX() {
System.out.println("Class X method");
}
}
// Intermediate parent class Y extending X
class Y extends X {
public void methodY() {
System.out.println("Class Y method");
}
}
// Child class Z extending Y and containing the main method
public class Z extends Y {
public void methodZ() {
System.out.println("Class Z method");
}
public static void main(String[] args) {
// Instantiate the grandchild class Z
Z z = new Z();
// Calling method inherited indirectly from class X
z.methodX();
// Calling method inherited directly from parent class Y
z.methodY();
// Calling class Z's own local method
z.methodZ();
}
}
Expected Output:
Class X method Class Y method Class Z method
In this example program:
- Class Y extends class X, and class Z extends class Y. This establishes a multilevel inheritance hierarchy (X -> Y -> Z).
- Class Z directly inherits methodY() from its immediate superclass Y, and indirectly inherits methodX() from its grand-superclass X.
- An instance of class Z has access to non-private methods declared at every level of its inheritance chain, allowing it to invoke methodX(), methodY(), and methodZ() seamlessly.
Hierarchical Inheritance Program in Java
Write a Java program to demonstrate hierarchical inheritance where multiple subclasses derive from a single common superclass to share and reuse its non-private members.
Example Program 3: Hierarchical Inheritance
// Common superclass
class A {
public void msgA() {
System.out.println("Method of class A");
}
}
// First subclass extending class A
class B extends A {
// Inherits msgA() from class A
}
// Second subclass extending class A
class C extends A {
// Inherits msgA() from class A
}
// Third subclass extending class A
class D extends A {
// Inherits msgA() from class A
}
public class MyClass {
public static void main(String[] args) {
// Instantiate individual subclass objects
B obj1 = new B();
C obj2 = new C();
D obj3 = new D();
// Calling inherited method from common superclass A
obj1.msgA();
obj2.msgA();
obj3.msgA();
}
}
Expected Output:
Method of class A Method of class A Method of class A
In this example program:
- A single parent class A acts as the superclass for three independent subclasses (B, C, and D). This tree-like structure defines hierarchical inheritance.
- Rather than duplicating msgA() inside classes B, C, and D, all three classes inherit the method directly from class A. Thus, we achieve code reusability in the Java program.
- In the hierarchical inheritance, each subclass forms its own distinct branch. An instance of B has no relation to or access to class C, or D; they merely share the common functionality provided by A.
Behavior of Instance Variables in Inheritance (Variable Hiding)
In Java, instance variables are not polymorphic and cannot be overridden. When a subclass declares an instance variable with the same name as an inherited variable from its parent class, the child variable hides the parent variable.
In this scenario, the accessed variable is determined statically at compile time based on the reference type, rather than dynamically at runtime by the object type.
Write a Java program to demonstrate this field-hiding behavior.
Example Program 4: Instance Variable Hiding in Inheritance
// Superclass
class P {
int a = 30;
}
// Subclass hiding the variable 'a' of class P
class Q extends P {
int a = 50; // Hides P.a
}
public class Test extends Q {
public static void main(String[] args) {
// Reference of Q pointing to an object of Q
Q q = new Q();
System.out.println("Value of a: " + q.a);
// Reference of P pointing to an object of Q
P p = new Q();
System.out.println("Value of a: " + p.a);
}
}Expected Output:
Value of a: 50 Value of a: 30
In this example program:
- When a subclass declares an instance variable with the same name as an inherited instance variable from its superclass, it does not override it. Instead, the subclass variable hides the superclass variable. Both variables exist independently in memory inside the object instance.
- Unlike instance method calls (which use dynamic method dispatch based on the runtime object type), field access is resolved at compile time based strictly on the declared reference type.
- The reference variable q is of type Q. Therefore, the compiler binds q.a directly to the field a declared in class Q, printing 50.
- The reference variable p is of type P, even though it points to an instance of Q in the heap. Since field resolution depends purely on the reference type, the compiler binds p.a directly to the field a in class P, printing 30.
Behavior of Method Overriding in Inheritance
In Java, method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. Unlike variables, instance methods are polymorphic: when an overridden method is called, Java uses dynamic method dispatch at runtime to invoke the version belonging to the actual object, regardless of the reference type.
Write a Java program to demonstrate method overriding in inheritance, showing that the method executed at runtime depends on the actual object created rather than the reference type.
Example Program 5: Method Overriding
// Superclass
class Baseclass {
// Overridden method
void msg() {
System.out.println("Base class method");
}
}
// Subclass providing its own implementation
class Childclass extends Baseclass {
// Overriding method
@Override
void msg() {
System.out.println("Child class first method");
}
// Subclass-specific method
void msg2() {
System.out.println("Child class second method");
}
}
public class MyTest {
public static void main(String[] args) {
// Case 1: Subclass reference pointing to subclass object
Childclass obj = new Childclass();
obj.msg(); // Calls Childclass.msg()
obj.msg2(); // Calls Childclass.msg2()
// Case 2: Superclass reference pointing to subclass object (Upcasting)
Baseclass obj2 = new Childclass();
obj2.msg(); // Calls Childclass.msg() dynamically at runtime
// The line below causes a compile-time error:
// obj2.msg2(); // Error: method msg2() does not exist in Baseclass
}
}Expected Output:
Child class first method Child class second method Child class first method
Method overriding occurs only when a subclass provides a specific implementation of an inherited method from its superclass. The method name and parameter list (signature) must match exactly, while the return type must be identical or a subtype (covariant return type).
In this example program:
- Class Childclass overrides the msg() method inherited from Baseclass. The method signature (name, parameter list, and compatible return type) matches the parent declaration.
- Since obj is of type Childclass, invoking obj.msg() and obj.msg2() calls the implementations present in Childclass.
- A superclass reference can hold the memory address of any subclass object. At compile time, the compiler strictly enforces that only methods declared in the reference type (Baseclass) can be called through obj2.
- Even though obj2 is a Baseclass reference, Java resolves overridden instance method calls dynamically at runtime based on the actual object created in heap memory (Childclass). Therefore, Childclass.msg() is executed.
- Calling obj2.msg2() results in a compile-time error. Because msg2() is declared only in Childclass and does not exist in Baseclass, the compiler rejects the call based on the reference type.
Method Overriding with Instance Initializers and Constructors
In Java, method calls on an object are resolved polymorphically based on the runtime type of the instance, even while constructors and instance initializer blocks are executing.
Write a Java program to trace the execution order of instance initializer blocks, superclass constructors, and overridden methods during object instantiation.
Example Program 6:
// Superclass
class Hello {
// Instance initializer block
{
show();
}
// Superclass constructor
Hello() {
System.out.println("Hello constructor");
show();
}
void show() {
System.out.println("Hello method");
}
}
// Subclass overriding show()
class Hi extends Hello {
Hi() {
// Compiler implicitly inserts super(); here
System.out.println("Hi constructor");
}
@Override
void show() {
System.out.println("Hi method");
}
}
public class TestHelloHi extends Hi {
public static void main(String[] args) {
// Step 1: Instantiating subclass Hi
Hi obj = new Hi();
obj.show();
// Step 2: Instantiating subclass Hi via superclass reference
Hello obj1 = new Hi();
obj1.show();
}
}Expected Output:
Hi method Hello constructor Hi method Hi constructor Hi method Hi method Hello constructor Hi method Hi constructor Hi method
In this example:
- When new Hi() is invoked, the Hi() constructor begins execution. Since there is no explicit constructor call, the compiler automatically inserts an implicit super(); as the first statement, which transfers control to the superclass constructor Hello().
- Before the body of the Hello() constructor runs, all instance initializer blocks in class Hello are executed in order. The instance block calls show(). Because the actual object being created is an instance of Hi, dynamic method dispatch binds this call to Hi.show(), printing “Hi method”.
- Next, the body of Hello() constructor executes. It prints “Hello constructor” and then calls show() method. Since the runtime object is Hi, it calls Hi.show() again, printing “Hi method”.
- After the superclass constructor finishes, execution returns to the body of Hi(), which prints “Hi constructor”.
- Calling obj.show() on the Hi reference directly invokes Hi’s overridden method, printing “Hi method”.
- Executing new Hi() repeats the exact same constructor and initialization sequence as step 1, generating the first four output lines again.
- Calling obj1.show() on the superclass reference obj1 still executes Hi.show() because Java resolves overridden instance methods dynamically at runtime based on the actual object created (Hi), printing “Hi method”.
Behavior of Overloaded Method in Inheritance
In Java, method overloading is resolved statically at compile time based on the declared reference type and the argument types passed. A subclass can overload a method inherited from its parent class by defining a method with the same name but a different parameter list. In this scenario, the superclass method remains accessible in the subclass through inheritance.
Write a Java program to demonstrate method overloading across an inheritance hierarchy and observe how reference types determine method availability at compile time.
Example Program 7: Overloaded Methods in Inheritance
// Superclass
class Animal {
// Zero-parameter method
void food() {
System.out.println("What kind of food do lions eat?");
}
}
// Subclass overloading the inherited food() method
class Lion extends Animal {
// Overloaded method (different parameter list)
void food(int x) {
System.out.println("Lions eat flesh");
}
}
public class LionTest {
public static void main(String[] args) {
// Superclass reference pointing to subclass object
Animal a = new Lion();
a.food(); // Calls inherited Animal.food()
// a.food(20); // Compile-time error: Animal class has no food(int) method
// Subclass reference pointing to subclass object
Lion l = new Lion();
l.food(); // Calls inherited food() from Animal
l.food(10); // Calls overloaded food(int) from Lion
}
}Expected Output:
What kind of food do lions eat? What kind of food do lions eat? Lions eat flesh
In this example:
- The reference variable a is of type Animal.
- At compile time, the compiler searches Animal for a food() method with zero arguments. It finds it and binds the call.
- At runtime, since Lion does not override food(), the implementation in Animal executes, printing: “What kind of food do lions eat?”.
- The line a.food(20); results in a compile-time error. Even though the runtime object is Lion (which has a food(int) method), the compiler checks the call strictly against the declared reference type (Animal). Since the parent class Animal declares no matching food(int) method, compilation fails.
- Since Lion extends Animal, the zero-argument food() method is inherited and accessible through reference l.
- When l.food(); is executed, the compiler finds the zero-argument food() method available in class Lion via inheritance from Animal.
- Since Lion does not override this method, the implementation in class Animal is executed, printing “What kind of food do lions eat?”.
- The reference l is of type Lion. Calling l.food(10) matches the overloaded method signature food(int x) declared directly in Lion, printing: “Lions eat flesh”.
Scenario-Based Java Inheritance Program
In Java inheritance, how members are accessed depends on whether you are accessing a variable or calling a method:
- Instance Variables: Resolved statically at compile time based on the reference type (Field Hiding).
- Overridden Instance Methods: Resolved dynamically at runtime based on the actual object type in memory (Dynamic Method Dispatch).
- Reference Assignment: A superclass reference can point to a subclass object (upcasting), but a subclass reference cannot directly point to a superclass object without explicit and compatible casting.
Write a Java program demonstrating all possible reference-to-object assignment scenarios between a superclass and a subclass in inheritance.
Example Program 8: Base Classes Setup
// Superclass
class AA {
int x = 20;
int y = 30;
void msg1() {
System.out.println("I am msg1 in class AA");
}
void msg2() {
System.out.println("I am msg2 in class AA");
}
}
// Subclass
class BB extends AA {
int y = 50; // Hides AA.y (Field Hiding)
int z = 60; // Subclass-specific field
// Overriding method
@Override
void msg2() {
System.out.println("I am msg2 in class BB");
}
// Subclass-specific method
void msg3() {
System.out.println("I am msg3 in class BB");
}
}Scenario 1: Superclass Reference Pointing to Superclass Object (AA a = new AA())
public class Scenario1 {
public static void main(String[] args) {
AA a = new AA();
System.out.println("Value of x: " + a.x); // 20
System.out.println("Value of y: " + a.y); // 30
// System.out.println(a.z); // Compile error: 'z' does not exist in AA
a.msg1(); // Executes AA.msg1()
a.msg2(); // Executes AA.msg2()
// a.msg3(); // Compile error: 'msg3()' does not exist in AA
}
}Expected Output:
Value of x: 20 Value of y: 30 I am msg1 in class AA I am msg2 in class AA
Explanation: The reference and object are both of type AA. The instance has no knowledge of subclass BB, so only members declared in AA are accessible.
Scenario 2: Subclass Reference Pointing to Subclass Object (BB b = new BB())
public class Scenario2 {
public static void main(String[] args) {
BB b = new BB();
System.out.println("Value of x: " + b.x); // 20 (inherited from AA)
System.out.println("Value of y: " + b.y); // 50 (BB.y hides AA.y)
System.out.println("Value of z: " + b.z); // 60 (declared in BB)
b.msg1(); // Executes AA.msg1() (inherited)
b.msg2(); // Executes BB.msg2() (overridden)
b.msg3(); // Executes BB.msg3() (declared in BB)
}
}Expected Output:
Value of x: 20 Value of y: 50 Value of z: 60 I am msg1 in class AA I am msg2 in class BB I am msg3 in class BB
Explanation: Class BB has access to all its own members as well as inherited non-private members of AA. Because BB declares its own y, it hides AA.y, making b.y print 50.
Scenario 3: Superclass Reference Pointing to Subclass Object (AA a = new BB())
public class Scenario3 {
public static void main(String[] args) {
AA a = new BB();
// Variables resolved by reference type (AA)
System.out.println("Value of x: " + a.x); // 20
System.out.println("Value of y: " + a.y); // 30 (resolved from AA, not BB)
// Methods resolved polymorphically by runtime object (BB)
a.msg1(); // Executes AA.msg1() (inherited by BB)
a.msg2(); // Executes BB.msg2() (overridden version in BB)
// Subclass-specific members are not accessible through reference type AA:
// System.out.println(a.z); // Compile error: 'z' is not declared in AA
// a.msg3(); // Compile error: 'msg3()' is not declared in AA
}
}Expected Output:
Value of x: 20 Value of y: 30 I am msg1 in class AA I am msg2 in class BB
Explanation:
- Fields: Resolved at compile time by the reference type (AA), so a.y accesses AA.y (30), demonstrating field hiding.
- Methods: Resolved at runtime by the object type (BB), so a.msg2() executes the overridden version in BB.
- Visibility: The compiler restricts access strictly to members declared in the reference type (AA), so z and msg3() cannot be accessed directly.
Scenario 4: Reassigning Subclass Reference to Superclass Reference (a = b)
public class Scenario4 {
public static void main(String[] args) {
AA a = new AA();
BB b = new BB();
a = b; // Upcasting: 'a' now points to the object referenced by 'b'
System.out.println("Value of x: " + a.x); // 20
System.out.println("Value of y: " + a.y); // 30 (resolved by reference AA)
a.msg1(); // Executes AA.msg1()
a.msg2(); // Executes BB.msg2() (dynamic method dispatch)
}
}Expected Output:
Value of x: 20 Value of y: 30 I am msg1 in class AA I am msg2 in class BB
Explanation: Assigning a = b updates reference a to point to the BB object on the heap. This behaves identically to Scenario 3 (AA a = new BB()).
Scenario 5: Subclass Reference Pointing to Superclass Object (BB b = new AA())
public class Scenario5 {
public static void main(String[] args) {
// BB b = new AA(); // Compile-time Error: Type mismatch (incompatible types)
}
}Explanation: This causes a compile-time error. A child class reference cannot directly refer to a parent class object because a superclass object does not have the members and capabilities promised by the child class contract (e.g., fields z or methods msg3()).
Scenario 6: Downcasting (b = (BB) a)
public class Scenario6 {
public static void main(String[] args) {
AA a = new BB(); // Upcasting
BB b = (BB) a; // Downcasting: reference 'b' points to the same BB object
// Accessing via subclass reference 'b'
System.out.println("Value of y via b: " + b.y); // 50 (BB.y)
System.out.println("Value of z via b: " + b.z); // 60 (BB.z)
b.msg3(); // Executes BB.msg3()
// Accessing via superclass reference 'a'
System.out.println("Value of y via a: " + a.y); // 30 (AA.y)
a.msg2(); // Executes BB.msg2()
}
}Expected Output:
Value of y via b: 50 Value of z via b: 60 I am msg3 in class BB Value of y via a: 30 I am msg2 in class BB
Explanation: Casting (BB) a does not create a new object. It merely creates a new reference b of type BB pointing to the exact same object in the heap. Through b, you regain access to BB’s hidden fields (b.y = 50) and subclass-specific members (b.z and b.msg3()).
Wrapping Up
In this guide, we covered a wide variety of Java inheritance example programs with in-depth explanations to help you prepare for technical interviews. These practice problems are essential for both freshers and experienced developers preparing for coding tests and viva rounds. Keep these rules and core OOP concepts in mind.






