Java Inheritance Quiz: 30 MCQs (Basic to Advanced)

Welcome to this comprehensive Java inheritance quiz! Here, we have compiled the top 30 multiple-choice questions (MCQs) from basic to advanced concepts of inheritance. We have covered the following topics:

Take your time analyzing each code snippet, trace the execution path carefully, and solidify your core Java OOP skills.

Which keyword is used in Java to create a subclass that inherits from a superclass?
A. implements
B. extends
C. inherits
D. super
extends
Explanation:
extends is the keyword used to establish an inheritance relationship between a subclass and its superclass, allowing the subclass to inherit fields and methods.
What will be the output of the following code?
class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}
class Cat extends Animal {
    @Override
    void makeSound() {
        super.makeSound();
        System.out.println("Cat meows");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal myCat = new Cat();
        myCat.makeSound();
    }
}
A. Animal makes a sound
B. Cat meows
C. Animal makes a sound
Cat meows
D. Cat meows
Animal makes a sound
Animal makes a sound
Cat meows
Explanation:
When myCat.makeSound() is called, Java uses dynamic method dispatch to invoke Cat’s overridden makeSound(). Inside it, super.makeSound() runs first (printing “Animal makes a sound”), followed by the System.out.println(“Cat meows”) statement.Key points: Even though the reference type is Animal, the actual object is a Cat, so Java calls the overridden version of makeSound() at runtime (polymorphism). The super.makeSound() call explicitly invokes the parent class’s version from within the child’s overridden method, rather than replacing it.
Given the following Java program, what is the exact output when the main method is executed?
class Vehicle {
    Vehicle() {
        System.out.println("Vehicle constructor called");
    }

    Vehicle(String type) {
        System.out.println("Vehicle constructor called: " + type);
    }
}
class Car extends Vehicle {
    Car() {
        System.out.println("Car constructor called");
    }
}
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car();
    }
}
A. Car constructor called
B. Vehicle constructor called
Car constructor called
C. Vehicle constructor called: Car
Car constructor called
D. Compilation error
Vehicle constructor called
Car constructor called
Explanation:
Since Car’s constructor does not explicitly call super(…), the Java compiler automatically inserts an implicit call to super() — the no-argument constructor of Vehicle — as the first statement. This prints “Vehicle constructor called” first, followed by “Car constructor called”.
What will be the output of the following code?
class Shape {
    double area(double side) {
        return side * side;
    }
}
class Square extends Shape {
    double area(double side) {
        System.out.println("Calculating square area");
        return side * side;
    }
    double area(double side, double unused) {
        System.out.println("Overloaded method called");
        return side * side;
    }
}
public class Main {
    public static void main(String[] args) {
        Shape s = new Square();
        System.out.println(s.area(4));
    }
}
A. Calculating square area
16.0
B. Overloaded method called
16.0
C. 16.0
D. Compilation error
Calculating square area
16.0
Explanation:
s is a Shape reference, but it points to a Square object. When we call s.area(4), Java looks at the actual object (Square) at runtime, not just the reference type. Since Square has overridden the one-parameter area(double side) method, that version executes, printing “Calculating square area” and returning 16.0.Key points:

  • Overriding means a child class rewrites a parent method with the same name and same parameters. Java decides which version to run based on the real object, not the reference type. This happens automatically at runtime.
  • Overloading means having multiple methods with the same name but different parameters. Java decides which overloaded method to run based on what arguments you pass in, and this is decided at compile time, not by the actual object type.
The following code fails to compile. What is the reason, and which option correctly identifies the fix?
class Employee {
    String name;

    Employee(String name) {
        this.name = name;
        System.out.println("Employee constructor called for: " + name);
    }
}
class Manager extends Employee {
    Manager() {
        System.out.println("Manager constructor called");
    }
}
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager();
    }
}
A. The code fails because Manager must also declare a field named name; adding String name; inside Manager fixes it.
B. The code fails because Manager does not override the Employee constructor; adding @Override above Manager() fixes it.
C. The code fails because Java does not allow subclasses to have constructors with no parameters; renaming Manager() to Manager(String name) fixes it.
D. The code fails because Employee has no no-argument constructor, and Manager() implicitly calls super(); adding super(name); with an explicit argument, such as super(“John”);, inside Manager() fixes it.
The code fails because Employee has no no-argument constructor, and Manager() implicitly calls super(); adding super(name); with an explicit argument, such as super(“John”);, inside Manager() fixes it.
Explanation:
Since Employee only defines a parameterized constructor (Employee(String name)), no implicit no-argument constructor exists. Because Manager() doesn’t explicitly call super(…), Java tries to insert an implicit super() call — but no matching no-argument constructor exists in Employee, causing a compilation error. The fix is to explicitly call a valid superclass constructor, such as super(“John”);, as the first line of Manager().Key points: If a superclass does not provide a no-argument constructor, every subclass constructor must explicitly call one of the superclass’s available constructors using super(…) as its first statement; otherwise, compilation fails. This is a very common real-world error when refactoring superclasses to require constructor arguments.
What will be the output of the following code?
class A {
    A() {
        System.out.println("A constructor");
    }
}
class B extends A {
    B() {
        System.out.println("B constructor");
    }
}
class C extends B {
    C() {
        System.out.println("C constructor");
    }
}
public class Main {
    public static void main(String[] args) {
        C obj = new C();
    }
}
A. C constructor
B constructor
A constructor
B. A constructor
B constructor
C constructor
C. A constructor
C constructor
D. C constructor
A constructor
B constructor
C constructor
Explanation:
When we create a C object, Java goes all the way up to the topmost parent (A) first. So it runs A’s constructor, then B’s constructor, then finally C’s constructor. This is because each constructor secretly calls super() at the start, even though we don’t write it.Key points: When one class inherits from another, which inherits from another (like C → B → A), Java always builds the object starting from the very top parent, then moves down step by step to the child. So the order is always: oldest parent first, then next parent, then the child last.
Review the following Java code snippet. What will be printed to the console when the main method is executed?
abstract class Shape {
    abstract double area();

    void display() {
        System.out.println("Area is: " + area());
    }
}
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return 3.14 * radius * radius;
    }
}
public class Main {
    public static void main(String[] args) {
        Shape s = new Circle(2);
        s.display();
    }
}
A. Area is: 12.56
B. Compilation error: cannot instantiate Shape
C. Area is: 0.0
D. Compilation error: Circle must implement display()
Area is: 12.56
Explanation:
Shape is abstract, so we cannot create a Shape object directly — but we didn’t. We created a Circle object and stored it in a Shape reference, which is allowed. Circle provides a real version of area(), so when display() calls area(), it correctly calls Circle’s version. The math is 3.14 * 2 * 2 = 12.56.
Which of the following statements about inheritance in Java is correct?
A. A subclass can access the private members (fields and methods) of its superclass directly, just like public members.
B. A subclass inherits the public and protected members of its superclass, but not its private members.
C. Java allows a class to extend more than one class at the same time (multiple inheritance of classes).
D. Inheritance is only useful for reusing methods; fields cannot be inherited.
A subclass inherits the public and protected members of its superclass, but not its private members.
Explanation:
When a class extends another class, it gets access to the parent’s public and protected members. It does not get direct access to private members — those stay locked inside the parent class only.
A subclass has a field with the same name as a private field in its superclass. What happens when you access this field through a superclass reference pointing to a subclass object?
A. The subclass field value is used.
B. The superclass field value is used, because field access is resolved by reference type, not object type.
C. A compilation error occurs.
D. A runtime exception occurs.
The superclass field value is used because field access is resolved by reference type, not object type.
Explanation:
Fields in Java do not use dynamic dispatch like methods do. Field access is decided at compile time based on the reference type. So if you access a field through a superclass reference, you always get the superclass field, even if the actual object is a subclass. This is called field hiding, and it works differently from method overriding, which always uses the real object’s version at runtime.

class Parent {
    String label = "Parent label";
}

class Child extends Parent {
    String label = "Child label";
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.label); // Prints "Parent label"
    }
}
What does the instanceof operator check in Java?
A. Whether a variable is null.
B. Whether an object belongs to a given class or any of its subclasses.
C. Whether two objects have the same field values.
D. Whether a method has been overridden.
Whether an object belongs to a given class or any of its subclasses.
Explanation:
The instanceof operator checks the actual type of an object at runtime. It returns true if the object is an instance of the given class or any class that inherits from it. This is commonly used before casting an object to a more specific subclass to avoid runtime errors.

class Animal {}
class Dog extends Animal {}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.println(a instanceof Dog);    // true
        System.out.println(a instanceof Animal);  // true
    }
}
What happens when the following code runs?
class Animal {}
class Dog extends Animal {}

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal();
        Dog d = (Dog) a;
        System.out.println("Cast successful");
    }
}
A. It prints “Cast successful”.
B. It throws a ClassCastException at runtime.
C. It fails to compile.
D. It prints “null”.
It throws a ClassCastException at runtime.
Explanation:
The object being created is an Animal, not a Dog. Even though the code compiles because Dog is a subclass of Animal, so the cast looks valid to the compiler, Java checks the real object type at runtime. Since the actual object is an Animal and not a Dog, the cast fails and throws a ClassCastException. This is why it is safer to check with `instanceof` before casting.
What is the effect of declaring a method as final in a superclass?
A. The method can be called only once during the program.
B. Subclasses cannot override that method.
C. The method automatically becomes static.
D. The method cannot accept any parameters.
Subclasses cannot override that method.
Explanation:
The final keyword, when used on a method, locks that method’s implementation. Any subclass that tries to override it will get a compilation error.

class Payment {
    final void process() {
        System.out.println("Processing payment securely");
    }
}

class CardPayment extends Payment {
    // void process() { } // This would cause a compilation error
}
What happens if a class is declared as final and another class tries to extend it?
A. It works normally, final only affects methods.
B. A compilation error occurs.
C. A runtime exception occurs.
D. The subclass silently ignores the inherited members.
A compilation error occurs.
Explanation:
When a class is marked final, it means the class cannot be extended at all. Any attempt to write class Sub extends FinalClass will fail to compile. This is often used for classes that should never be changed through inheritance, such as Java’s built-in String class.

final class Config {
    void show() {
        System.out.println("Config settings");
    }
}

class CustomConfig extends Config { 
    // Compilation error: cannot inherit from final class Config
}
What will be the output of the following code?
class Parent {
    static void greet() {
        System.out.println("Parent static method");
    }
}
class Child extends Parent {
    static void greet() {
        System.out.println("Child static method");
    }
}
public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.greet();
    }
}
A. Parent static method
B. Child static method
C. Compilation error
D. Runtime exception
Parent static method
Explanation:
Static methods are not overridden in Java, they are hidden. This means the version that runs depends on the reference type, not the actual object, unlike normal instance methods. Since `p` is declared as type `Parent`, calling `p.greet()` runs Parent’s static method, even though the object is actually a Child.
When overriding a method, which return type rule is correct in modern Java?
A. The overriding method must return the exact same type as the parent method.
B. The overriding method can return a subtype of the parent method’s return type (covariant return type).
C. The overriding method can return any type, regardless of the parent method.
D. The overriding method cannot return an object type.
The overriding method can return a subtype of the parent method’s return type (covariant return type).
Explanation:
Java allows what is called a covariant return type. This means an overriding method can return a more specific type than the parent method, as long as that type is a subclass of the original return type. This makes the code more useful, because callers get a more specific object without needing to cast it.

class Animal {
    Animal reproduce() {
        return new Animal();
    }
}

class Dog extends Animal {
    @Override
    Dog reproduce() { // Returns Dog instead of Animal, which is allowed
        return new Dog();
    }
}
Which of the following is not allowed when overriding a method in Java?
A. Increasing the access level of the method, such as from protected to public.
B. Reducing the access level of the method, such as from public to protected.
C. Adding a covariant return type.
D. Removing a throws clause from the method.
Reducing the access level of the method, such as from public to protected.
Explanation:
When overriding a method, you cannot reduce the access level of the method, such as from protected to public.

class Parent {
    public void show() {
        System.out.println("Parent show");
    }
}

class Child extends Parent {
    // protected void show() { } // Compilation error: cannot reduce visibility
}
A superclass method declares throws IOException. Which overriding rule is correct for checked exceptions?
A. The overriding method must throw the same checked exception or a broader one.
B. The overriding method can throw the same checked exception, a subclass of it, or no checked exception at all, but not a broader one.
C. The overriding method cannot throw any exceptions.
D. Checked exceptions have no rules in method overriding.
The overriding method can throw the same checked exception, a subclass of it, or no checked exception at all, but not a broader one.
Explanation:
An overriding method does not throw new or broader checked exceptions than the method it overrides. It is allowed to throw the same exception, a more specific (subclass) exception, or none at all. This rule protects code that calls the parent method through a parent reference, since it only expects the exceptions declared in the parent method.

import java.io.IOException;
import java.io.FileNotFoundException;
class Loader {
    void load() throws IOException {
        System.out.println("Loading");
    }
}
class FileLoader extends Loader {
    @Override
    void load() throws FileNotFoundException { // Allowed, FileNotFoundException is a subclass of IOException
        System.out.println("Loading file");
    }
}
Consider the following two Java source files located in different packages:
// File: com/company/core/Repository.java
package com.company.core;
public class Repository {
    private int id = 101;
    String name = "DefaultRepo";
    protected String status = "ACTIVE";
    public int capacity = 500;

    protected void sync() {
        System.out.println("Syncing repository");
    }
}
// File: com/company/service/CloudRepository.java
package com.company.service;
import com.company.core.Repository;

public class CloudRepository extends Repository {
    public void printDetails(Repository otherRepo) {
        // Line 1: Accessing through inheritance
        System.out.println(this.status);
        this.sync();

        // Line 2: Accessing via parameter reference
        System.out.println(otherRepo.status);

        // Line 3: Accessing package-private field
        System.out.println(this.name);
    }
}

Which statement correctly identifies the compilation result of CloudRepository.java?

A. The code compiles successfully without any compilation errors.
B. Compilation fails on Line 3 only because name has package-private access and CloudRepository is in a different package.
C. Compilation fails on Line 2 and Line 3 because status cannot be accessed via a parent reference from outside its package, and name is package-private.
D. Compilation fails on Line 1, Line 2, and Line 3 because protected members are completely inaccessible across different packages.
Compilation fails on Line 2 and Line 3 because the status cannot be accessed via a parent reference from outside its package, and name is package-private.

Explanation:

  • Line 1 is valid: A subclass in a different package inherits protected members and can access them via this (or implicitly on the subclass instance).
  • Line 2 causes a compile error: Per the Java Language Specification, a subclass in a different package can only access a protected member of its superclass if the access is performed through a reference that is either of that subclass type or one of its subtypes. Accessing otherRepo.status on a direct Repository reference from a different package is not permitted.
  • Line 3 causes a compile error: The field name has default (package-private) access, which restricts visibility strictly to classes within the com.company.core package.
Examine the following Java code consisting of an inheritance hierarchy:
class PaymentGateway {
    public final void processPayment(double amount) {
        validateTransaction(amount);
        execute(amount);
    }

    protected void validateTransaction(double amount) {
        System.out.print("Base Validation ");
    }
    private final void logTransaction() {
        System.out.print("Base Log ");
    }
    protected void execute(double amount) {
        System.out.print("Standard Transfer ");
    }
}

class FastPaymentGateway extends PaymentGateway {
    @Override
    protected void validateTransaction(double amount) {
        System.out.print("Fast Validation ");
    }
    // Line A
    public void logTransaction() {
        System.out.print("Fast Log ");
    }
    // Line B
    @Override
    public final void execute(double amount) {
        System.out.print("Instant Transfer ");
    }
}

Which statement accurately describes the compilation and execution behavior of these classes?

A. Compilation fails at Line A because final methods in a superclass can never be redeclared with the same signature in any subclass.
B. Compilation fails at Line B because an overridden method cannot add the final modifier if the superclass method was not marked final.
C. The code compiles successfully; Line A declares a new independent method rather than overriding a final method, and Line B is a valid final override.
D. Compilation fails at both Line A and Line B due to conflicting modifier declarations between the superclass and subclass.
The code compiles successfully; Line A declares a new independent method rather than overriding a final method, and Line B is a valid final override.

Explanation:

  • Line A compiles cleanly: A private method in a parent class is not part of the inherited API contract, so defining a method with the same signature in the child class does not count as overriding.
  • Line B compiles cleanly: execute(double amount) is protected and non-final in PaymentGateway, so overriding it and making it both public (broadening access is legal) and final is fully compliant with the Java Language Specification.
In the following code, what will shapes[1].area() return?
abstract class Shape {
    abstract double area();
}
class Square extends Shape {
    double side = 4;
    double area() { return side * side; }
}
class Circle extends Shape {
    double radius = 3;
    double area() { return 3.14 * radius * radius; }
}
public class Main {
    public static void main(String[] args) {
        Shape[] shapes = { new Square(), new Circle() };
        System.out.println(shapes[1].area());
    }
}
A. 0.0, because Shape’s area() always runs.
B. 28.26, because dynamic dispatch calls Circle’s overridden area() method.
C. A compilation error, since Shape[] cannot hold Circle objects.
D. A runtime exception.
28.26, because dynamic dispatch calls Circle’s overridden area() method.
Explanation:
An array of the parent type can hold objects of any subclass, since a subclass object is also considered a type of its parent. When a method is called on an array element, Java checks the actual object’s class at runtime and runs its overridden version.
What is the correct order of execution when an object of a subclass is created, including instance initializer blocks?
A. Subclass constructor, subclass instance blocks, superclass constructor, superclass instance blocks.
B. Superclass instance blocks, superclass constructor, subclass instance blocks, subclass constructor.
C. Superclass constructor, superclass instance blocks, subclass constructor, subclass instance blocks.
D. All instance blocks run first, then both constructors run together.
Superclass instance blocks, superclass constructor, subclass instance blocks, subclass constructor.
Explanation:
Java always fully sets up the parent part of an object before the child part. Within each class, the order is: instance initializer blocks run first, right before the constructor’s own body runs (but after the implicit or explicit `super()` call). So overall, the flow is: parent’s instance blocks, then parent’s constructor body, then child’s instance blocks, then child’s constructor body.

class Parent {
    { System.out.println("Parent instance block"); }
    Parent() { System.out.println("Parent constructor"); }
}
class Child extends Parent {
    { System.out.println("Child instance block"); }
    Child() { System.out.println("Child constructor"); }
}
public class Main {
    public static void main(String[] args) {
        new Child();
    }
}
A superclass field is declared protected. Which classes can access it directly?
A. Only classes in the exact same file.
B. Classes in the same package, and subclasses even in different packages.
C. Every class in the entire project, regardless of package or inheritance.
D. No class can access a protected field directly, it always needs a getter method.
Classes in the same package, and subclasses even in different packages.
Explanation:
The protected access modifier is broader than the default (package-private) access. It allows access from any class in the same package, and it also allows subclasses to access the member even if they live in a completely different package.

// File: base/Vehicle.java
package base;
public class Vehicle {
    protected int speed = 100;
}
// File: extended/SportsCar.java
package extended;
import base.Vehicle;

public class SportsCar extends Vehicle {
    void showSpeed() {
        System.out.println(speed); // Allowed, because of protected access
    }
}
What will be the output of the following code?
class Parent {
    static void greet() {
        System.out.println("Parent static method");
    }
}
class Child extends Parent {
    static void greet() {
        System.out.println("Child static method");
    }
}
public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.greet();
    }
}
A. Child static method
B. Parent static method
C. Compilation error
D. Both methods run, one after another
Child static method
Explanation:
Since the reference variable c is declared as type Child, and static methods are resolved based on the reference type at compile time, calling c.greet() runs Child’s version. This shows that static method resolution always depends on the declared type of the variable used to call it.
Why does the following code fail to compile?
class Animal {}
class Bird extends Animal {}
class Fish extends Animal {}

public class Main {
    public static void main(String[] args) {
        Animal a = new Bird();
        System.out.println(a instanceof Fish); // Compiles fine, prints false
    }
}
A. Because Bird and Fish are unrelated classes, and Java cannot check the cast at compile time.
B. Because instanceof cannot be used with interfaces.
C. Because Animal is missing a constructor.
D. The code actually compiles fine.
The code actually compiles fine.
Explanation:
This code compiles without any issue. Fish extends Animal, and a is declared as Animal, so checking a instanceof Fish is completely valid, since a could be holding any subclass of Animal, including Fish. The compiler only rejects instanceof checks between two classes that have no possible relationship at all, such as two unrelated sibling classes with no shared type.
Why can a Java class only extend one other class, unlike some other object-oriented languages?
A. Java’s compiler cannot handle more than one parent class technically.
B. Java avoids multiple class inheritance mainly to prevent ambiguity, such as conflicting inherited methods from two parents.
C. Java does not support inheritance at all in this case.
D. Multiple inheritance is allowed in Java, but only for abstract classes.
Java avoids multiple class inheritance mainly to prevent ambiguity, such as conflicting inherited methods from two parents.
Explanation:
Some languages allow a class to inherit from more than one class at the same time, but this can create confusion. For example, if two parent classes both have a method with the same name but different behavior, the compiler would not clearly know which one to use. Java avoids this problem entirely by only allowing a class to extend one parent class, keeping the hierarchy simple and predictable. Shared behavior from multiple sources is instead handled using interfaces.

class A {}
class B {}

// class C extends A, B { } // Not allowed in Java, causes a compilation error
A subclass overrides a method but forgets to include the `@Override` annotation. What is the risk of skipping this annotation?
A. The method will not run at all without the annotation.
B. If the method signature accidentally does not match the parent method, the compiler will not catch the mistake, and a new method is silently created instead of overriding.
C. The program will always crash at runtime.
D. The annotation is required by Java, so the code will not compile without it.
If the method signature accidentally does not match the parent method, the compiler will not catch the mistake, and a new method is silently created instead of overriding.
Explanation:
The @Override annotation is optional, but strongly recommended. If it is included, the compiler checks that the method truly matches a method in the parent class. Without it, a small mistake, such as a typo in the method name or a slightly different parameter type, creates a brand new method instead of overriding the intended one, and Java will not warn you about it. This can lead to confusing bugs that are hard to notice.

class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    void makeSound(String extra) { // Typo: extra parameter, this is NOT overriding
        System.out.println("Bark " + extra);
    }
}
In real-world software design, why is inheritance often described using the phrase “is-a relationship”?
A. Because a subclass must always have the exact same fields as its superclass.
B. Because a subclass should represent a more specific version of its superclass, meaning every subclass object can be treated as a superclass object.
C. Because inheritance is only used to reduce the number of files in a project.
D. Because Java requires this naming pattern for compilation.
Because a subclass should represent a more specific version of its superclass, meaning every subclass object can be treated as a superclass object.
Explanation:
Good inheritance design follows the idea that a subclass “is a” more specific type of its superclass. For example, a Manager is a type of Employee, so `Manager extends Employee` makes sense. This relationship guarantees that anywhere an `Employee` is expected, a `Manager` object can be used safely, since it truly behaves like an Employee, just with extra features. If this relationship does not naturally make sense, inheritance is usually the wrong design choice, and composition may fit better instead.

class Employee {
    void work() {
        System.out.println("Doing regular work");
    }
}

class Manager extends Employee {
    void approveLeave() {
        System.out.println("Approving leave request");
    }
}

public class Main {
    public static void main(String[] args) {
        Employee e = new Manager(); // Valid, because a Manager "is an" Employee
        e.work();
    }
}
In a banking system, “SavingsAccount” and “CurrentAccount” both extend a common “Account” class. Which design decision best follows good inheritance practice?
A. Put shared logic, such as balance and deposit(), in the Account class, and let each subclass define its own specific rules, such as interest or overdraft handling.
B. Copy the balance and deposit() logic separately into both SavingsAccount and CurrentAccount, without a shared parent class.
C. Make SavingsAccount extend CurrentAccount, since they are similar.
D. Avoid inheritance completely and put all logic in one single class using many if-else conditions.
Put shared logic, such as balance and deposit(), in the Account class, and let each subclass define its own specific rules, such as interest or overdraft handling.
Explanation:
Good inheritance design places common, shared behavior in the parent class, so it is written only once and reused everywhere. Each subclass then only needs to add or override the behavior that makes it unique. This keeps the code organized, avoids duplication, and makes future changes easier, since shared logic only needs to be updated in one place.

class Account {
    double balance;

    void deposit(double amount) {
        balance += amount;
    }
}

class SavingsAccount extends Account {
    double interestRate = 0.03;

    void addInterest() {
        balance += balance * interestRate;
    }
}

class CurrentAccount extends Account {
    double overdraftLimit = 500;
}
What does the Liskov Substitution Principle say about inheritance, and why does it matter?
A. A subclass must always have more methods than its superclass.
B. A subclass must always override every method from its superclass.
C. It states that inheritance should never be used in real projects.
D. A subclass object should be usable anywhere a superclass object is expected, without breaking the program’s expected behavior.
A subclass object should be usable anywhere a superclass object is expected, without breaking the program’s expected behavior.
Explanation:
The Liskov Substitution Principle is a well-known software design rule that says subclasses should behave in a way that does not surprise code written for the superclass. If replacing a superclass object with a subclass object causes unexpected errors or wrong behavior, the inheritance design is likely flawed. This principle helps developers create reliable, predictable class hierarchies, and it is a common topic in technical interviews.

class Bird {
    void fly() {
        System.out.println("Flying");
    }
}

// Violates the principle, because Penguin cannot truly fly like a Bird is expected to
class Penguin extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("Penguins cannot fly");
    }
}
Carefully inspect the following executable Java program:
class Base {
    public Number compute(int x) {
        System.out.print("Base:int ");
        return x;
    }
    public void process(Object obj) {
        System.out.print("Base:Object ");
    }
}
class Derived extends Base {
    // Method 1
    public Integer compute(Integer x) {
        System.out.print("Derived:Integer ");
        return x;
    }
    // Method 2
    @Override
    public Double compute(int x) {
        System.out.print("Derived:int ");
        return (double) x;
    }
    // Method 3
    public void process(String str) {
        System.out.print("Derived:String ");
    }
}
public class TrickyInheritance {
    public static void main(String[] args) {
        Base ref = new Derived();
        Derived concrete = new Derived();

        ref.compute(10);
        concrete.compute(10);
        concrete.compute(Integer.valueOf(10));

        ref.process("Java");
        concrete.process((Object) "Java");
    }
}

What is the exact output printed when executing this program?

A. Base:int Derived:int Derived:Integer Base:Object Base:Object
B. Derived:int Derived:Integer Derived:Integer Derived:String Derived:String
C. Compilation fails because Derived attempts to change the return type of compute(int) to Double.
D. Derived:int Derived:int Derived:Integer Base:Object Base:Object
Derived:int Derived:int Derived:Integer Base:Object Base:Object

Explanation:

  • Covariant Return Types: Derived.compute(int) overrides Base.compute(int). In Java 5+, an overriding method can return a subtype of the superclass method’s return type (Double is a subtype of Number). Therefore, the override is completely valid.
  • ref.compute(10): The compiler checks reference type Base, finds compute(int). At runtime, late binding dispatches to the overridden method in Derived. Prints: Derived:int .
  • concrete.compute(10): The compiler searches Derived for compute matching an int literal. Primitive exact match (compute(int)) takes strict precedence over boxing conversion (compute(Integer)). Prints: Derived:int .
  • concrete.compute(Integer.valueOf(10)): Passing an Integer object matches the overloaded Derived.compute(Integer) method directly. Prints: Derived:Integer .
  • ref.process(“Java”): Overloading is resolved statically at compile time based on the reference type. Because ref is of type Base, the compiler only sees Base.process(Object). At runtime, Derived does not override process(Object), it merely overloads it with process(String). Thus, Base.process(Object) executes. Prints: Base:Object .
  • concrete.process((Object) “Java”): Even though the runtime object and string are instances of String, the explicit cast to (Object) causes compile-time method resolution to bind to process(Object). Prints: Base:Object .

DEEPAK GUPTA

DEEPAK GUPTA

Deepak Gupta is the Founder of Scientech Easy, a Full Stack Developer, and a passionate coding educator with 8+ years of professional experience in Java, Python, web development, and core computer science subjects. With strong expertise in full-stack development, he provides hands-on training in programming languages and in-demand technologies at the Scientech Easy Institute, Dhanbad.

He regularly publishes in-depth tutorials, practical coding examples, and high-quality learning resources for both beginners and working professionals. Every article is carefully researched, technically reviewed, and regularly updated to ensure accuracy, clarity, and real-world relevance, helping learners build job-ready skills with confidence.