Java Inheritance & Polymorphism Interview Questions and Answers (2026) Interview Questions | JiQuest

add

#

Java Inheritance & Polymorphism Interview Questions and Answers (2026)

BACKEND INTERVIEW PREPARATION
Java Inheritance & Polymorphism
Master every inheritance and polymorphism concept asked in Java interviews — from classic IS-A / HAS-A relationships and method overriding rules to Java 17 sealed classes, dynamic dispatch, pattern matching instanceof, the Liskov Substitution Principle, diamond problem, bridge methods, and the complete Object class contract. 100+ expert Q&As covering beginner to advanced topics.
⏳ 50 min read 📝 100+ Q&As 🎯 Beginner to Advanced
⚡ Quick Reference
Inheritance TypeSingle, Multilevel, Hierarchical, Multiple (via interfaces)
IS-A vs HAS-AIS-A = extends/implements; HAS-A = composition (field reference)
Method OverridingSame signature in subclass; resolved at runtime (dynamic dispatch)
Method HidingStatic methods with same signature; resolved at compile time
super keywordAccess parent constructor, method, or field from subclass
Covariant ReturnSubclass override can return a subtype of the parent return type
Sealed ClassesJava 17: restricts which classes can extend/implement a type
LSPLiskov Substitution: subtype must be usable wherever supertype expected
Pattern Matching instanceofJava 16+: if (obj instanceof String s) { ... } — no explicit cast
Bridge MethodsCompiler-generated methods to preserve polymorphism with generics
Java Inheritance Hierarchy & Polymorphism Flow
Object (java.lang)
Abstract Class
partial impl, state
Interface
contract, default methods
Concrete Subclass
overrides methods
Sealed Subclass (Java 17)
permitted only
Runtime Polymorphism
Dynamic Dispatch
Compile-time Polymorphism
Method Overloading
instanceof / Pattern Match
Type-safe casting
LSP Conformance

Java Inheritance & Polymorphism Interview Questions & Answers

Q1. What is inheritance in Java and what are its types?

A: Inheritance is an OOP mechanism whereby a subclass acquires the properties (fields) and behaviours (methods) of a parent class using the extends keyword (for classes) or implements (for interfaces). Java supports four logical types:
1. Single — one class extends one parent.
2. Multilevel — a chain: A ← B ← C.
3. Hierarchical — multiple subclasses share one parent: A ← B, A ← C.
4. Multiple — achieved only via interfaces (a class implements two or more interfaces).
Java deliberately forbids multiple class inheritance to avoid the diamond problem.

Q2. What is the difference between IS-A and HAS-A relationships?

A: IS-A represents an inheritance relationship — a Dog IS-A Animal (class Dog extends Animal). It models a "kind of" relationship and is expressed using extends or implements.
HAS-A represents a composition/aggregation relationship — a Car HAS-A Engine (the Car class holds a field of type Engine). It models a "contains" relationship and is the preferred design choice when the relationship is not a strict type hierarchy. Composition is generally favoured over inheritance for flexibility and lower coupling.

Q3. What is method overriding? What are its rules?

A: Method overriding occurs when a subclass provides its own implementation for a method already defined in the parent class, with the same method signature (name + parameter types). Rules:
1. Return type must be the same or a covariant (subtype).
2. Access modifier cannot be more restrictive than the parent's.
3. The method must not be static, final, or private.
4. The overriding method can throw unchecked exceptions freely, but checked exceptions must be the same or narrower than those declared by the parent.
5. Use @Override annotation to let the compiler validate the override.

class Animal {
    // can throw IOException
    public String sound() { return "..."; }
}

class Dog extends Animal {
    @Override
    public String sound() { return "Woof"; } // valid override
}

Animal a = new Dog();
System.out.println(a.sound()); // "Woof" — runtime dispatch

Q4. What is method hiding? How does it differ from overriding?

A: Method hiding applies to static methods. When a subclass declares a static method with the same signature as a static method in the parent, it hides it rather than overrides it. The key difference: overriding is resolved at runtime based on the object's actual type (dynamic dispatch), while hiding is resolved at compile time based on the reference type.

class Parent {
    public static void greet() { System.out.println("Parent"); }
    public void hello()        { System.out.println("Parent hello"); }
}
class Child extends Parent {
    public static void greet() { System.out.println("Child"); }  // hiding
    @Override
    public void hello()        { System.out.println("Child hello"); } // overriding
}

Parent p = new Child();
p.greet();  // "Parent"  — compile-time resolution (reference type)
p.hello();  // "Child hello" — runtime resolution (object type)

Q5. What are the three uses of the super keyword?

A:
1. super() — constructor chaining: calls the parent class constructor. Must be the first statement in the subclass constructor.
2. super.method(): calls an overridden method from the parent class within the subclass.
3. super.field: accesses a hidden field from the parent class (when the subclass declares a field with the same name).

class Vehicle {
    String type = "Vehicle";
    Vehicle(String name) { System.out.println("Vehicle: " + name); }
    void describe() { System.out.println("I am a vehicle"); }
}

class Car extends Vehicle {
    String type = "Car";
    Car(String name) {
        super(name);                      // 1. super() constructor
        System.out.println("Car: " + name);
    }
    void describe() {
        super.describe();                 // 2. super.method()
        System.out.println("I am a car");
    }
    void showTypes() {
        System.out.println(super.type);   // 3. super.field — "Vehicle"
        System.out.println(this.type);    // "Car"
    }
}

Q6. What are covariant return types?

A: Introduced in Java 5, a covariant return type allows an overriding method to declare a return type that is a subtype of the return type declared in the parent method. This avoids unnecessary casting at the call site.

class Animal {
    public Animal create() { return new Animal(); }
}
class Dog extends Animal {
    @Override
    public Dog create() { return new Dog(); } // covariant: Dog is a subtype of Animal
}

Dog d = new Dog().create(); // no cast needed

Q7. What does the final keyword do in the context of inheritance?

A:
- final class: cannot be subclassed (e.g., java.lang.String, Integer). Any attempt to extend it causes a compile error.
- final method: cannot be overridden in any subclass. Useful for security-sensitive or template-method steps that must not change.
- final field: must be initialised once (in declaration or constructor) and cannot be reassigned — not directly related to inheritance but affects what subclasses can do with the field.

Q8. What are sealed classes introduced in Java 17?

A: Sealed classes (finalised in Java 17 after preview in 15/16) let you explicitly declare which classes or interfaces are permitted to extend or implement them using the permits clause. Every permitted subclass must be either final, sealed, or non-sealed. This gives library authors precise control over the class hierarchy and enables exhaustive pattern matching in switch expressions.

public sealed class Shape permits Circle, Rectangle, Triangle {}

public final class Circle    extends Shape { double radius; }
public final class Rectangle extends Shape { double w, h; }
public non-sealed class Triangle extends Shape {} // open for further extension

// Exhaustive switch (Java 21 pattern switch)
double area = switch (shape) {
    case Circle c    -> Math.PI * c.radius * c.radius;
    case Rectangle r -> r.w * r.h;
    case Triangle t  -> 0; // must handle all permitted subtypes
};

Q9. What is the difference between an abstract class and an interface for inheritance purposes?

A:

AspectAbstract ClassInterface
Multiple inheritanceNo (single extends)Yes (multiple implements)
State (fields)Yes, any modifierOnly public static final constants
ConstructorsYesNo
Default methodsConcrete methods allowedYes (since Java 8)
Access modifiersAnyMethods public by default
When to useShared state + partial implPure contract / capability

Q10. What is the Liskov Substitution Principle (LSP)?

A: LSP (the "L" in SOLID) states: objects of a supertype should be replaceable with objects of a subtype without altering the correctness of the program. In practice:
- Subclass methods should not strengthen preconditions (require more) or weaken postconditions (guarantee less).
- Subclasses must honour invariants of the parent class.
Classic violation: making a Square extends Rectangle where setWidth also changes height — code expecting an independent width/height breaks.

Q11. How does runtime polymorphism work in Java (dynamic dispatch)?

A: At runtime, the JVM resolves a virtual method call by looking at the actual type of the object on the heap, not the declared type of the reference. This is called dynamic method dispatch. The JVM uses a virtual method table (vtable) per class, which stores pointers to the most-specific implementation of each virtual method. When a subclass overrides a method, its vtable entry points to the overriding method, so even through a parent reference the subclass method is invoked.

Q12. What is the instanceof operator and how does pattern matching improve it in Java 16+?

A: instanceof tests whether an object is an instance of a given type at runtime. Before Java 16, you had to cast separately:
if (obj instanceof String) { String s = (String) obj; ... }
Java 16 introduced pattern matching for instanceof: if (obj instanceof String s) { ... } — the variable s is a pattern variable scoped to the true branch, eliminating the redundant cast and reducing ClassCastException risk.

Object obj = "Hello Java 16";

// Old way
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// Pattern matching (Java 16+)
if (obj instanceof String s) {
    System.out.println(s.length()); // s is String here, no cast
}

// Can also combine conditions
if (obj instanceof String s && s.length() > 5) {
    System.out.println("Long string: " + s);
}

Q13. What methods does the Object class provide that are relevant to inheritance?

A: Every Java class implicitly extends java.lang.Object. Key methods:
- equals(Object o) — logical equality; default is reference equality (==).
- hashCode() — integer hash for use in hash-based collections; must be consistent with equals.
- toString() — string representation; default is ClassName@hexHashCode.
- clone() — shallow copy; class must implement Cloneable.
- getClass() — returns runtime Class object; final, cannot be overridden.
- wait(), notify(), notifyAll() — thread synchronisation on an object's monitor.
- finalize() — deprecated since Java 9; called before GC.

Q14. What is constructor chaining with super() and what are the rules?

A: When a subclass object is created, a parent constructor must run first to initialise the parent portion of the object. Rules:
1. If you do not explicitly call super(), the compiler inserts super() (no-arg) automatically.
2. If the parent has no no-arg constructor and you don't call a specific parent constructor, it is a compile error.
3. super() must be the first statement in the subclass constructor — you cannot put any statement before it.
4. this() (same-class chaining) and super() cannot both appear in the same constructor.

Q15. What are the access modifier rules when overriding a method?

A: An overriding method must not reduce visibility — it can only maintain or increase it. Allowed escalations:

Parent modifierAllowed in override
privateNot overridable (compiler does not see it as override)
package-private (default)package-private, protected, public
protectedprotected, public
publicpublic only

Q16. What is the pitfall of calling an overridden method from a constructor?

A: If a parent constructor calls a method that is overridden in a subclass, and a subclass object is being constructed, the subclass's overriding method executes before the subclass constructor body runs — meaning subclass fields are at their default values (?, 0, null, false). This is a well-known source of bugs.

class Base {
    Base() {
        printValue(); // calls overridden method — dangerous!
    }
    void printValue() { System.out.println("Base"); }
}

class Derived extends Base {
    int value = 42;
    Derived() { super(); } // implicit
    @Override
    void printValue() {
        System.out.println("Value=" + value); // prints "Value=0", not 42!
    }
}
// new Derived() prints: Value=0  (field not yet initialised)

Q17. How does polymorphism work with arrays and collections?

A: Java arrays are covariant: a Dog[] is a subtype of Animal[]. This is safe for reading but can cause ArrayStoreException at runtime when writing. Collections with generics use invariance by default — List<Dog> is NOT a subtype of List<Animal>. Use wildcards (? extends Animal) for read-only access or (? super Dog) for write access (PECS principle).

Q18. What is the diamond problem and how does Java resolve it?

A: The diamond problem occurs when a class inherits from two classes that both inherit from a common ancestor — the subclass gets two copies of the ancestor's state, causing ambiguity. Java avoids it by forbidding multiple class inheritance. With interfaces (which have no state), Java 8+ handles default method conflicts: if two interfaces provide the same default method, the implementing class must override it to resolve the conflict explicitly.

interface A { default String hello() { return "A"; } }
interface B { default String hello() { return "B"; } }

// Compile error without override:
class C implements A, B {
    @Override
    public String hello() {
        return A.super.hello(); // explicitly call A's version
    }
}

Q19. What are bridge methods and why does the compiler generate them?

A: Bridge methods are synthetic methods generated by the Java compiler to preserve polymorphism when generics are involved. Due to type erasure, a generic method's type parameter is replaced by its bound (usually Object). If a subclass overrides a generic method with a more specific type, the compiler creates a bridge method with the erased signature that delegates to the specific override, ensuring that the vtable-based dispatch still works correctly at runtime.

class Comparable<T> { int compareTo(T o) { return 0; } }

class MyNum extends Comparable<Integer> {
    @Override
    public int compareTo(Integer o) { return 0; }
    // Compiler also generates bridge:
    // public synthetic int compareTo(Object o) { return compareTo((Integer)o); }
}
// javap -verbose MyNum shows the ACC_BRIDGE ACC_SYNTHETIC method

Q20. What are the rules for equals() and hashCode() in an inheritance hierarchy?

A: The general contracts:
- equals(): reflexive, symmetric, transitive, consistent, and x.equals(null) returns false.
- hashCode(): equal objects must have equal hash codes; unequal objects should ideally have different hash codes.
In inheritance, correctly implementing equals() becomes tricky because symmetry and transitivity can be violated. The standard advice (Effective Java Item 10) is: use getClass() check (strict) or instanceof check (lenient, violates symmetry with subclasses). Never add significant fields to a subclass that participates in equals — prefer composition instead.

Q21. How does the toString() method interact with inheritance?

A: Object.toString() returns getClass().getName() + "@" + Integer.toHexString(hashCode()). Subclasses should override it to provide meaningful output. String concatenation with + and System.out.println() automatically call toString(). In a hierarchy, each class typically calls super.toString() and appends its own fields to build a complete picture.

Q22. What is the clone() method and the Cloneable interface?

A: Object.clone() is a protected native method that creates a shallow copy of the object. To use it, a class must implement the marker interface Cloneable; otherwise CloneNotSupportedException is thrown. In inheritance: every subclass that wants to support cloning must override clone() as public and call super.clone(), which propagates up to Object.clone(). Shallow cloning copies references, not the referred objects — deep cloning requires additional logic. Many modern designs favour copy constructors or static factory methods over clone().

Q23. What is the difference between single and multilevel inheritance?

A: Single inheritance: Class B extends Class A — one direct parent.
Multilevel inheritance: Class C extends Class B, which extends Class A — forms a chain. In multilevel inheritance, C inherits from both B and A. All public/protected members from the entire chain are available. The deepest class's constructor chains through all intermediate constructors via implicit or explicit super() calls.

Q24. What is hierarchical inheritance?

A: Hierarchical inheritance is when multiple subclasses share the same parent class. For example, classes Dog, Cat, and Bird all extend Animal. Each subclass has its own specialised implementation but shares the common behaviour/state defined in Animal. Changes to Animal affect all subclasses, which is a key consideration in design.

Q25. Why does Java not support multiple class inheritance?

A: Java disallows multiple class inheritance primarily to avoid the diamond problem — ambiguity when two parent classes both inherit from a common grandparent, resulting in duplicated state and unclear method resolution. It also simplifies the language and JVM implementation. Java achieves the benefits of multiple inheritance (behaviour reuse) through interfaces, which before Java 8 had no state or method bodies, and since Java 8 through default methods with explicit conflict resolution rules.

Q26. Can a constructor be inherited in Java?

A: No. Constructors are not inherited. A subclass cannot directly call a parent's constructor as if it were its own, but it can invoke it using super(). The subclass must define its own constructors. If no constructor is defined, the compiler adds a default no-arg constructor that calls super().

Q27. Can private methods be overridden?

A: No. Private methods are not visible to subclasses and therefore cannot be overridden. If a subclass defines a method with the same signature as a private parent method, it is simply a new independent method in the subclass — not an override. The @Override annotation on such a method causes a compile error, which is a useful safety net.

Q28. What happens when a subclass hides a field from the parent?

A: Fields can be hidden (not overridden) when a subclass declares a field with the same name as a parent field. Field access is resolved at compile time based on the reference type, not runtime type. This is unlike methods. Accessing via a parent reference gives the parent field; accessing via a subclass reference or this gives the subclass field. super.fieldName accesses the parent's field. Hiding fields is considered bad practice and should be avoided.

Q29. What is an abstract class and when should you use it?

A: An abstract class is declared with the abstract keyword and cannot be instantiated directly. It may have abstract methods (declared without body) that must be implemented by concrete subclasses, and also concrete methods. Use an abstract class when:
- Subclasses share significant state or common behaviour.
- You want to provide a template method pattern (a skeleton algorithm).
- You need protected constructors or non-public API.
- You want to evolve the hierarchy without breaking existing subclasses (add concrete methods freely).

Q30. Can an abstract class have a constructor?

A: Yes. Although an abstract class cannot be instantiated directly, it can have constructors that are invoked when a concrete subclass is instantiated via super(). This is commonly used to initialise fields in the abstract class that subclasses share.

Q31. What is the difference between method overriding and method overloading?

A: Overriding is a runtime concept where a subclass provides a new implementation for an inherited method with the same signature. Overloading is a compile-time concept where multiple methods in the same class (or hierarchy) share the same name but differ in parameter list (number, type, or order). Overriding enables runtime polymorphism; overloading is a form of compile-time (ad-hoc) polymorphism.

Q32. What is the role of the @Override annotation?

A: @Override is a compile-time annotation that instructs the compiler to verify that the annotated method truly overrides a method in a superclass or implements a method from an interface. If it does not (e.g., due to a typo in the method name or wrong parameter types), the compiler reports an error. It also improves code readability by making overrides explicit. Always use it.

Q33. Can you override a static method?

A: No. Static methods belong to the class, not to instances, and are resolved at compile time based on the reference type. You can declare a static method with the same name in a subclass, but that is hiding, not overriding. The @Override annotation on a static method causes a compile error.

Q34. What is the Object class and its position in the hierarchy?

A: java.lang.Object is the root of the entire Java class hierarchy. Every class implicitly extends Object (if no explicit superclass is declared). This means every Java object has access to equals(), hashCode(), toString(), clone(), getClass(), wait(), notify(), and notifyAll().

Q35. What is the getClass() method and how does it relate to polymorphism?

A: getClass() is a final native method that returns the runtime Class object of an object. Because it is final, it cannot be overridden. It is used to get the actual type at runtime — useful for equals() implementations and reflection. It returns the most-derived class of the object regardless of the reference type, illustrating runtime polymorphism at the metadata level.

Q36. How do you implement the equals() contract correctly in a subclass?

A: Best practice (Effective Java):
1. Check if (this == o) return true.
2. Check if (!(o instanceof MyClass)) return false — or use getClass() != o.getClass() for strict type equality.
3. Cast and compare each significant field using Objects.equals() for objects and == for primitives.
4. Always override hashCode() consistently.

class Point {
    int x, y;
    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Point)) return false;
        Point p = (Point) o;
        return x == p.x && y == p.y;
    }
    @Override public int hashCode() {
        return Objects.hash(x, y);
    }
}

Q37. What is the Comparable interface and how is it used in inheritance?

A: Comparable<T> defines the natural ordering of a class via the compareTo(T o) method. When a class implements Comparable, its instances can be sorted by Collections.sort(), Arrays.sort(), and used in sorted collections. In a hierarchy, if a parent implements Comparable<Parent>, the subclass inherits compareTo but can override it. Careful with LSP: the subclass's comparison must remain consistent with the parent's contract.

Q38. What is the Comparator interface and how does it differ from Comparable?

A: Comparator<T> defines an external ordering strategy for objects. Unlike Comparable (which is intrinsic to the class), a Comparator is a separate object. Multiple Comparators can define multiple orderings for the same class. In Java 8+, Comparators can be built using lambda expressions and composed with thenComparing(). Use Comparator when you don't own the class or need alternative orderings.

Q39. What is protected access and how does it behave across packages in inheritance?

A: protected access means the member is accessible within the same package AND in subclasses regardless of package. However, within a subclass in a different package, you can only access the protected member through an instance of the subclass (or its subtypes), not through a parent-class reference. This nuance often trips up candidates.

Q40. Can varargs methods be overridden? What are the pitfalls?

A: Yes, a varargs method can be overridden. The compiler treats void foo(String... args) as void foo(String[] args) at the bytecode level. When overriding, the signature must match (both declared as varargs or both as array). A common pitfall: if a subclass overrides a varargs method with a fixed-arity version, method resolution may produce unexpected results due to varargs being a lower priority match during overload resolution.

Q41. What is generic method inheritance?

A: Generic methods defined in a superclass are inherited by subclasses. The subclass can call them with specific type arguments. If the subclass overrides a generic method, it can narrow the type parameter or use the same generic signature. Due to type erasure, the override is validated at the erased signature level, and bridge methods are generated by the compiler as needed.

Q42. What is type erasure and how does it affect inheritance with generics?

A: Type erasure is the process by which the Java compiler removes generic type parameters at runtime, replacing them with their bounds (usually Object). This means at runtime, a List<String> and List<Integer> are both just List. In inheritance, when a class extends a generic superclass with a specific type argument (class StringBox extends Box<String>), the compiler generates bridge methods so that the erased superclass method delegates to the specific subclass method.

Q43. What is the difference between IS-A and LIKE-A relationships?

A: IS-A is a strict inheritance relationship where the subtype truly is a specialisation of the supertype and can substitute for it (LSP holds). LIKE-A (also called "uses-a" or "behaves-like") is when a class mimics another's interface but isn't a true subtype — for example, a Stack implemented by extending Vector (Java's classic mistake) gives a Stack that also exposes add() and remove() at arbitrary positions, violating stack semantics. Composition is preferred in LIKE-A cases.

Q44. What is the Template Method design pattern and how does it relate to inheritance?

A: The Template Method pattern defines the skeleton of an algorithm in an abstract base class, deferring some steps to subclasses. The base class's template method (often final) calls abstract or hook methods that subclasses implement. This is a direct application of inheritance-based polymorphism — the parent controls the algorithm flow while subclasses customise specific steps without being able to alter the overall structure.

Q45. What is the difference between early binding and late binding in Java?

A: Early binding (static binding): method call is resolved at compile time. Applies to static methods, private methods, final methods, and overloaded methods. Late binding (dynamic binding): method call is resolved at runtime based on the object's actual type. Applies to overridden instance methods. Late binding is the mechanism that enables runtime polymorphism. The JVM implements it via vtables.

Q46. How does Java handle default method conflicts from multiple interfaces?

A: If a class implements two interfaces that both provide a default method with the same signature, the class must override the method to resolve the conflict — otherwise it's a compile error. Inside the override, the class can call a specific interface's default method using InterfaceName.super.methodName(). If one interface extends another and overrides the default method, the more specific interface's version wins for implementing classes.

Q47. What is the difference between composition and inheritance?

A: Inheritance (IS-A): creates a tight coupling — changes to the parent class may break subclasses; exposes parent's internals to subclass; promotes code reuse through extension.
Composition (HAS-A): the class contains a reference to another object; looser coupling; changes to the contained object only affect the outer class through its interface; supports the "prefer composition over inheritance" principle (Effective Java Item 18). Composition is more flexible because you can swap out the contained object at runtime.

Q48. Can interfaces extend other interfaces?

A: Yes. An interface can extend one or more interfaces using the extends keyword. An implementing class must provide implementations for all abstract methods from the entire interface hierarchy. Interface inheritance follows the same IS-A principle.

interface Flyable { void fly(); }
interface Swimmable { void swim(); }
interface Duck extends Flyable, Swimmable { void quack(); }

class MallardDuck implements Duck {
    public void fly()   { System.out.println("Flying"); }
    public void swim()  { System.out.println("Swimming"); }
    public void quack() { System.out.println("Quack!"); }
}

Q49. What is the purpose of the non-sealed modifier in Java 17 sealed hierarchies?

A: In a sealed hierarchy, every permitted subclass must be declared as final (no further extension), sealed (restricts its own subclasses), or non-sealed (open for any further extension, like an ordinary class). The non-sealed keyword is used to "re-open" a branch of the hierarchy, allowing unknown external classes to extend that node while keeping the rest of the hierarchy controlled.

Q50. How does pattern matching work with sealed classes in switch expressions (Java 21)?

A: Java 21 finalised pattern matching for switch, which integrates with sealed hierarchies. When switching over a sealed type, the compiler can verify exhaustiveness — you must handle every permitted subtype (or have a default). This enables safe, concise type-dispatching without explicit casts.

sealed interface Expr permits Num, Add, Mul {}
record Num(int val)       implements Expr {}
record Add(Expr l, Expr r) implements Expr {}
record Mul(Expr l, Expr r) implements Expr {}

int eval(Expr e) {
    return switch (e) {
        case Num(int v)      -> v;
        case Add(var l, var r) -> eval(l) + eval(r);
        case Mul(var l, var r) -> eval(l) * eval(r);
    }; // exhaustive — no default needed
}

Q51. What is the difference between abstract methods and default methods in interfaces?

A: An abstract method in an interface has no body and must be implemented by any concrete class that implements the interface. A default method (introduced in Java 8) has a body and provides a default implementation that implementing classes inherit automatically; they may override it but are not required to. Default methods allow interfaces to evolve (add new methods) without breaking existing implementing classes.

Q52. What are static methods in interfaces and are they inherited?

A: Since Java 8, interfaces can have static methods with bodies. Unlike default methods, interface static methods are NOT inherited by implementing classes or subinterfaces. They must be called on the interface name directly: MyInterface.staticMethod(). This prevents ambiguity in multiple-inheritance scenarios and serves as utility methods tied to the interface's contract.

Q53. What is the difference between polymorphism and abstraction?

A: Abstraction is the process of hiding complex implementation details and exposing only the essential interface — achieved via abstract classes and interfaces. Polymorphism is the ability of different objects to respond to the same message (method call) in different ways — achieved through method overriding and dynamic dispatch. Abstraction defines the "what," polymorphism provides the "how varies by type."

Q54. Can you reduce the visibility of an inherited method in a subclass?

A: No. An overriding method cannot reduce the visibility of the parent method. For example, if the parent declares a method as public, the override must also be public. Reducing visibility would violate the substitutability principle: code holding a parent reference would fail when trying to call the method on a subclass object. The compiler enforces this rule.

Q55. What is the difference between casting and instanceof?

A: instanceof checks the actual runtime type of an object and returns a boolean. It is safe — it never throws an exception (except with null, where it returns false). Casting ((TargetType) obj) attempts to convert the reference type; if the actual object is not of that type, a ClassCastException is thrown at runtime. The recommended practice is to always use instanceof (or pattern matching instanceof in Java 16+) before casting.

Q56. What is a virtual method in Java?

A: In Java, every non-static, non-private, non-final instance method is implicitly virtual — it participates in dynamic dispatch through the vtable mechanism. The JVM resolves which implementation to call based on the actual runtime type of the object. This is in contrast to languages like C++ where you must explicitly mark methods as virtual. Java's default-virtual approach is why polymorphism works so naturally.

Q57. What is the difference between upcasting and downcasting?

A: Upcasting: treating a subclass reference as a superclass reference. Always safe and done implicitly: Animal a = new Dog();. Downcasting: treating a superclass reference as a subclass reference. Must be done explicitly and may throw ClassCastException if the actual object is not the target subclass: Dog d = (Dog) a;. Always check with instanceof before downcasting.

Q58. What happens to static members in inheritance?

A: Static members (fields and methods) belong to the class, not to instances. Subclasses inherit static members of the parent in the sense that they can access them via the subclass name, but they don't override them — they hide them. Static member access uses the reference type at compile time, not the runtime type. Static fields are shared across all instances of the declaring class, regardless of which subclass reference is used to access them.

Q59. What is the significance of the final modifier on a method in terms of performance?

A: Marking a method final prevents overriding, which allows the JIT (Just-In-Time) compiler to inline the method call — replacing it with the method body directly at the call site — eliminating the vtable lookup overhead. This can be a minor performance optimisation, but modern JVMs are very good at inlining even non-final methods through speculative optimisation, so the practical difference is usually negligible. Prefer final for correctness/design reasons rather than performance.

Q60. What is interface segregation principle and how does it relate to inheritance?

A: The Interface Segregation Principle (ISP — the "I" in SOLID) states that clients should not be forced to depend on interfaces they do not use. In Java inheritance terms: rather than one large interface that all implementors must provide (even methods irrelevant to them), split it into smaller focused interfaces. Classes then implement only the relevant interfaces. This reduces the blast radius of changes and is the motivating reason for Java's many small functional interfaces (Runnable, Callable, Supplier, Consumer, etc.).

Q61. What is the decorator pattern and how does it use inheritance and composition?

A: The Decorator pattern wraps an object to add behaviour without modifying the original class. A Decorator class implements (or extends) the same interface/abstract class as the component and holds a reference to a component instance (HAS-A). It delegates to the wrapped object and adds behaviour before or after. This combines IS-A (implements the same interface) and HAS-A (wraps the component), demonstrating how inheritance and composition work together.

Q62. What are the rules for exceptions in overriding methods?

A: An overriding method:
- Can throw fewer or narrower checked exceptions than the parent.
- Cannot throw new or broader checked exceptions than the parent declares (compile error).
- Can freely throw any unchecked exceptions (RuntimeException subclasses) regardless of what the parent declares.
- Can throw no exceptions at all.
This rule ensures that code using the parent type can safely handle exceptions without needing to know the subclass.

Q63. How is polymorphism achieved with interfaces?

A: When multiple classes implement the same interface, you can hold references of the interface type and invoke the interface methods polymorphically. At runtime, the actual implementing class's method is called. This is the backbone of Java's dependency injection, strategy pattern, and virtually all framework APIs (e.g., List, Comparator, Runnable).

Q64. What is the difference between nominal and structural typing in the context of Java's type system?

A: Java uses nominal typing: a type relationship must be explicitly declared (a class must explicitly extend or implement to be considered a subtype). Structural typing (as in Go or TypeScript's duck typing) considers a type compatible if it has the required methods, regardless of any declared relationship. Java's explicit inheritance/implementation requirement prevents accidental subtyping but requires more boilerplate.

Q65. What is marker interface? Give examples and alternatives.

A: A marker interface has no methods — it marks a class as having a property. Examples: Cloneable, Serializable, RandomAccess. The JVM or framework checks for the marker with instanceof. Modern Java prefers annotations (@Entity, @Deprecated) as they are more expressive and can carry metadata. However, marker interfaces still have an advantage: they are part of the type system and can be used as type bounds in generics.

Q66. What is method resolution order (MRO) in Java when multiple interface default methods conflict?

A: Java's rules for resolving default method conflicts:
1. Classes win over interfaces: if the class (or any superclass) provides a concrete method matching the signature, it wins over any interface default.
2. More specific interface wins: if one interface extends another and overrides the default method, the subinterface's version is preferred.
3. Explicit override required: if two independent interfaces provide the same default method and neither rule above applies, the class must override it.

Q67. Can you override equals() in a way that breaks symmetry?

A: Yes, and this is a classic inheritance bug. If ColorPoint extends Point and ColorPoint.equals() checks color while Point.equals() does not, then point.equals(colorPoint) might return true while colorPoint.equals(point) returns false — violating symmetry. The solution is to either: (1) use getClass() check (strict but breaks Liskov), or (2) prefer composition (ColorPoint HAS-A Point) over inheritance.

Q68. What is the difference between shallow copy and deep copy in the context of clone()?

A: Shallow copy (default Object.clone()): primitive fields are copied by value; reference fields are copied by reference — both the original and clone point to the same sub-objects. Mutating a shared sub-object affects both. Deep copy: all reachable objects are recursively copied. In an inheritance chain, each class in the hierarchy must override clone() and call super.clone(), then deep-copy its own reference fields.

Q69. How do you prevent a class from being subclassed?

A: Several ways:
1. Declare the class final — the most explicit and common way.
2. In Java 17+, declare it sealed with no permitted subclasses (effectively the same, but more nuanced).
3. Make all constructors private — no subclass can call a parent constructor, preventing subclassing without a compile-time "cannot subclass" message (but it is effectively prevented).
4. Use a factory method pattern with package-private constructors.

Q70. What is the substitutability test for LSP?

A: To verify LSP compliance, ask: "Can I replace every instance of the parent class with an instance of the subclass in my existing code without any test failing?" If the answer is yes, LSP holds. Concrete checks:
- Preconditions (what the method requires): subclass must accept at least what parent accepts (not more restrictive).
- Postconditions (what the method guarantees): subclass must guarantee at least what parent guarantees (not weaker).
- Invariants: subclass must maintain all invariants of the parent class.

Q71. What is dynamic dispatch and how is it implemented by the JVM?

A: Dynamic dispatch is the mechanism by which the JVM selects which method implementation to invoke at runtime, based on the actual type of the object. The JVM maintains a virtual method table (vtable) per class — an array of method pointers. Each class inherits the parent's vtable and overwrites entries for overridden methods with its own implementations. An invokevirtual bytecode instruction looks up the method in the object's vtable, enabling polymorphic dispatch without explicit type checks.

Q72. What is the invokeinterface bytecode instruction and why is it different from invokevirtual?

A: invokevirtual dispatches instance method calls on class references using the vtable. invokeinterface dispatches method calls through an interface reference. The difference: with class references, the method's slot in the vtable is fixed at compile time; with interface references, the slot may differ in different implementing classes (since a class may implement many interfaces), so the JVM must do an itable (interface method table) lookup, which is slightly more expensive. Modern JVMs optimise this through inline caching.

Q73. How does the wait() and notify() mechanism work in the context of Object inheritance?

A: wait(), notify(), and notifyAll() are defined in Object and therefore every Java object can serve as a monitor. wait() releases the object's lock and suspends the calling thread. notify() wakes one waiting thread. notifyAll() wakes all waiting threads. They must be called from within a synchronized block on the same object. Because they're in Object, any instance can be a synchronisation point — a core design decision for Java's concurrency model.

Q74. What is the hashCode() contract and how does it apply in inheritance?

A: The contract: (1) multiple calls on the same object with no state change must return the same value; (2) if a.equals(b) is true, then a.hashCode() == b.hashCode() must be true; (3) if a.equals(b) is false, hash codes may differ (not required). In inheritance: if a subclass overrides equals() to include additional fields, it must override hashCode() to include those fields too. Failing to do so leads to broken HashMap/HashSet behavior (objects that are equal land in different buckets).

Q75. What are private interface methods and when were they introduced?

A: Private methods in interfaces were introduced in Java 9. They allow default and static methods in the interface to share helper code without exposing that helper as part of the public API. Private interface methods are not inherited by implementing classes. They improve encapsulation within the interface itself.

Q76. How does the super keyword work in interface default methods?

A: From within a class that implements an interface, you can call a specific interface's default method using InterfaceName.super.methodName(). You cannot use plain super to call an interface default from a subclass that doesn't implement the interface directly. Also, default methods cannot call super of their own interface (there is no "super" for interfaces).

Q77. Can an enum extend a class in Java?

A: No. Every enum implicitly extends java.lang.Enum, and since Java does not allow multiple class inheritance, an enum cannot explicitly extend any other class. However, an enum can implement one or more interfaces. Enum constants can override interface methods or provide their own bodies (anonymous class bodies for each constant), achieving polymorphism within the enum hierarchy.

Q78. What is an anonymous class and how does it relate to inheritance?

A: An anonymous class is a class without a name, declared and instantiated in a single expression. It always either extends a class or implements an interface. It's a form of single-use inheritance/implementation typically used to override methods on the fly (before lambdas were available). Anonymous classes close over effectively-final variables from their enclosing scope.

Runnable r = new Runnable() {  // anonymous class implementing Runnable
    @Override
    public void run() {
        System.out.println("Running anonymously");
    }
};
// Equivalent lambda (Java 8+):
Runnable r2 = () -> System.out.println("Running with lambda");

Q79. What is an inner class and how does it interact with inheritance?

A: A non-static inner class is implicitly associated with an instance of the outer class and can access all outer class members (including private). An inner class can extend any class or implement any interface, independently of the outer class hierarchy. If an inner class and the outer class both participate in inheritance hierarchies, they are separate hierarchies — the inner class's hierarchy does not affect the outer class's hierarchy.

Q80. What is the difference between inheriting and implementing in Java?

A: Inheriting (via extends) means acquiring state and behaviour from a class — one parent allowed. Implementing (via implements) means committing to a contract defined by an interface — multiple interfaces allowed. A class can simultaneously extend one class and implement multiple interfaces, combining both mechanisms.

Q81. What is the open-closed principle and how does inheritance support it?

A: The Open-Closed Principle (the "O" in SOLID) states: software entities should be open for extension but closed for modification. Inheritance supports this by allowing new functionality to be added by creating subclasses without changing the parent class. However, inheritance-based extension is brittle (tight coupling); interface-based extension (coding to abstractions) is generally preferred for OCP compliance.

Q82. What is a mixin and how can it be simulated in Java?

A: A mixin is a class that provides methods to other classes without being their parent in a strict IS-A sense. Java doesn't have native mixins, but they can be simulated using interface default methods — an interface provides default implementations that any implementing class "mixes in" without needing to extend a class. This is why Java 8 default methods are sometimes called "virtual extension methods" or mixins.

Q83. What happens when you call a final method from a subclass?

A: A final method is inherited by the subclass and can be called normally — it just cannot be overridden. The JVM can optimise final method calls using static dispatch (no vtable lookup needed), potentially inlining them. The subclass developer gets the guaranteed behaviour of the parent's method without risk of accidental override. Calling super.finalMethod() is valid but redundant since the subclass cannot have its own version.

Q84. What is the difference between polymorphism in Java and duck typing in Python?

A: Java polymorphism is nominal and type-safe: objects must explicitly declare their types through inheritance/interfaces, and type checking happens at both compile time and runtime. Duck typing (Python, JavaScript) is structural: if an object has the required method, it can be used regardless of class hierarchy — "if it walks like a duck and quacks like a duck, it's a duck." Java's approach catches type errors at compile time; duck typing is more flexible but defers errors to runtime.

Q85. What is a record and can it participate in inheritance?

A: Records (finalised in Java 16) are immutable data carriers. A record implicitly extends java.lang.Record and is implicitly final, so it cannot be subclassed. A record can implement interfaces. Since all records extend Record, they cannot extend any other class. This makes records unsuitable for class hierarchies but perfect for sealed hierarchies where each leaf is a record.

Q86. How do you call a grandparent class method when multilevel inheritance is involved?

A: You cannot directly call a grandparent's method from a grandchild using something like super.super.method() — Java does not support this syntax. The grandparent method must be exposed via the intermediate parent (e.g., the parent can provide a method that calls super.method(), and the grandchild calls that via super.parentHelper()). Alternatively, redesign to avoid needing grandparent method access directly.

Q87. What is the significance of the Serializable interface in inheritance?

A: Serializable is a marker interface. If a parent class is Serializable, its subclasses are automatically Serializable. If a class is Serializable but its superclass is not, the superclass's fields are not serialised; during deserialisation, the non-serialisable superclass's no-arg constructor is called. The serialVersionUID field should be defined explicitly to control version compatibility across the inheritance chain.

Q88. What is the difference between an interface with all default methods and an abstract class?

A: An interface with all default methods can be implemented by multiple classes (multiple inheritance of behaviour). An abstract class can only be extended by one class. The interface cannot have instance state (only static final constants) or constructors, while the abstract class can. Use the interface for capability mixins; use the abstract class when shared state or a constructor is needed.

Q89. What are the implications of inheriting a class that overrides equals() and hashCode()?

A: If a subclass adds fields that should be considered in equality but does not override equals() and hashCode(), two subclass objects differing only in the new fields will be considered equal (according to the parent's implementation). The subclass must override both if it adds significant state. Overriding only equals() but not hashCode() causes breakage in hash-based collections.

Q90. Can you instantiate an interface in Java?

A: No, you cannot directly instantiate an interface. However, you can create an instance of an anonymous class that implements the interface, or use a lambda expression (for functional interfaces). The resulting object's type is the anonymous class (or a hidden lambda class), but it is referred to via the interface reference.

Q91. What is the difference between polymorphism at compile time and at runtime?

A: Compile-time polymorphism (also called static polymorphism): resolved during compilation. Examples: method overloading, operator overloading (not in Java). The compiler picks the method based on the argument types. Runtime polymorphism (dynamic polymorphism): resolved during execution. Example: method overriding. The JVM picks the method based on the actual type of the object. Runtime polymorphism is more flexible but has a slight overhead compared to compile-time resolution.

Q92. What is the Object.finalize() method and why is it deprecated?

A: finalize() was called by the garbage collector before reclaiming an object. Subclasses could override it for cleanup. It was deprecated in Java 9 and removed in Java 18 because: (1) there is no guarantee when or if it is called; (2) it can cause object resurrection (re-attaching the object to a reference); (3) it has severe performance implications. The modern replacement is java.lang.ref.Cleaner or try-with-resources with AutoCloseable.

Q93. How does inheritance interact with the Singleton pattern?

A: The classic Singleton pattern (private constructor, static instance) prevents subclassing because the private constructor is not accessible. If you need a Singleton hierarchy, you can use a protected constructor and ensure each subclass also controls instantiation. However, mixing Singleton and inheritance is generally a design smell — consider an abstract factory or registry pattern instead.

Q94. What is the difference between ClassCastException and a compile-time type error?

A: A compile-time type error occurs when the compiler detects an impossible or unverifiable cast based on the static type system (e.g., casting a String to an Integer with no relationship). A ClassCastException occurs at runtime when the actual object is not of the target cast type, even though the compile-time check passed (e.g., an Animal reference holding a Cat object being cast to Dog). Pattern matching instanceof and proper use of generics reduce ClassCastException risk.

Q95. What is the role of the Cloneable interface in inheritance and what are its criticisms?

A: Cloneable is a marker interface that enables the protected Object.clone() to produce a field-by-field copy. Criticisms: (1) it changes the behavior of a method defined in Object rather than in the marker interface itself — a design flaw; (2) it does not declare a clone() method, so you cannot call clone() through a Cloneable reference polymorphically; (3) deep cloning requires significant manual code throughout the hierarchy; (4) better alternatives exist (copy constructors, copy factories).

Q96. What is the difference between a class implementing Comparable and using a Comparator in sorting a hierarchy?

A: If a parent class implements Comparable, subclasses inherit the natural ordering. Adding new fields in subclasses without changing compareTo means the subclass-specific fields are ignored in sorting. Adding them risks LSP violations. Comparator is more flexible: you can create specific Comparators for each subclass type. For heterogeneous hierarchies, Comparators are preferable because they can handle mixed types gracefully.

Q97. How do you detect if a method is overridden in a subclass at runtime using reflection?

A: Use the Reflection API:

boolean isOverridden(Object obj, String methodName, Class<?>... params) {
    try {
        Method m = obj.getClass().getMethod(methodName, params);
        // If the declaring class is not Object (or the parent), it is overridden
        return !m.getDeclaringClass().equals(Object.class);
    } catch (NoSuchMethodException e) {
        return false;
    }
}
// More specifically: check if declaring class equals the parent class
// m.getDeclaringClass() gives the class that declared or last overrode the method

Q98. What is PECS (Producer Extends Consumer Super) in the context of polymorphism with generics?

A: PECS is a guideline for using bounded wildcards with generics to maximise polymorphic flexibility:
- Producer Extends: use ? extends T when you only read (produce) from the collection. E.g., List<? extends Animal> lets you add Dog/Cat lists and read as Animal.
- Consumer Super: use ? super T when you only write (consume) to the collection. E.g., List<? super Dog> lets you add Dog or a Dog-accepting collection.
This is the correct application of polymorphism to Java's invariant generic type system.

Q99. What happens if a subclass doesn't call super() in its constructor?

A: If the superclass has a no-arg constructor, the compiler automatically inserts a super() call at the top of the subclass constructor. If the superclass only has parameterised constructors (no no-arg constructor), and the subclass constructor does not explicitly call one of them with super(args), it is a compile-time error. This ensures the parent portion of the object is always properly initialised.

Q100. What is the difference between inheriting state and inheriting behaviour?

A: Inheriting state: the subclass gets the parent's fields as part of its object layout. These fields are initialised by the parent's constructor. The subclass can access them if they are non-private. Inheriting behaviour: the subclass inherits the parent's methods and can invoke them. Overriding replaces behaviour; calling super.method() extends it. In good OOP design, you inherit behaviour through interfaces (contracts) and share state only within a tightly controlled class hierarchy.

Q101. What is method chaining in the context of inheritance and the builder pattern?

A: Method chaining requires that each method return this (or a suitable type). In inheritance with builders, a subclass builder that calls a parent builder's method would get back a parent builder reference, breaking the chain. The solution is to use a recursive generic type bound: class ParentBuilder<B extends ParentBuilder<B>> and return (B) this, so the subclass builder's methods return the subclass builder type.

Q102. What is the Curiously Recurring Template Pattern (CRTP) analogue in Java?

A: CRTP (common in C++) is simulated in Java via a self-referential generic type bound: class Base<T extends Base<T>>. Methods in Base can return T (the actual subclass type), enabling fluent/builder APIs without unchecked casts. It allows the base class to have methods that return the concrete subtype, preserving type safety in inheritance chains.

abstract class Builder<T extends Builder<T>> {
    String name;
    @SuppressWarnings("unchecked")
    public T withName(String name) { this.name = name; return (T) this; }
    public abstract Object build();
}

class PersonBuilder extends Builder<PersonBuilder> {
    int age;
    public PersonBuilder withAge(int age) { this.age = age; return this; }
    public Person build() { return new Person(name, age); }
}

Person p = new PersonBuilder().withName("Alice").withAge(30).build();

Q103. What is the difference between static and dynamic polymorphism performance-wise?

A: Static polymorphism (overloading) has no runtime overhead — the compiler has already resolved the exact method at compile time. Dynamic polymorphism (overriding) involves a vtable lookup per call, but modern JVMs use inline caching and monomorphic/bimorphic call sites to optimise: if the JVM observes that a call site always uses the same concrete type, it can inline the method call directly (devirtualisation), making the overhead negligible in practice.

Q104. What is the difference between method overriding and method specialisation?

A: Method overriding provides a completely different implementation for a method. Method specialisation (a concept from type theory) is a form of overriding where the subclass's implementation is a more specific version of the parent's that satisfies the same contract — a stricter form of LSP compliance. In practice, "specialisation" describes overriding that preserves and refines the parent's contract, while "overriding" is the general mechanism.

Q105. How do sealed classes enable exhaustive pattern matching and why is this important?

A: Because the compiler knows all permitted subtypes of a sealed hierarchy at compile time, it can verify that a switch expression (Java 21) or a series of pattern matches covers every possible subtype — a property called exhaustiveness. This is important because it turns incomplete handling from a runtime bug into a compile-time error. It brings type-safe algebraic data type (ADT) patterns (common in functional languages like Haskell and Scala) to Java, making state machines, AST visitors, and domain models safer and more expressive.

Q106. What is the difference between overriding toString() vs using a custom serialiser?

A: toString() is intended for human-readable debugging output. It should not be used for serialisation (converting to JSON, XML, binary) because it is not a stable contract — it can change between versions without notice. Custom serialisers (Jackson ObjectMapper, Gson, Java serialisation via Serializable, etc.) are designed for stable, reversible serialisation. In an inheritance hierarchy, serialisers can be configured to handle polymorphism (e.g., Jackson's @JsonTypeInfo) to preserve the actual subtype information.

Q107. What is the fragile base class problem?

A: The fragile base class problem occurs when changes to a superclass break subclasses, even though the change seems innocent in isolation. Because subclasses depend on implementation details (not just the public contract) of the superclass, adding, removing, or reordering method calls in the parent can cause unexpected subclass behaviour. This is why "prefer composition over inheritance" is a key design principle — composition depends only on the public interface of the component, which is more stable.

Q108. How do you test polymorphic behaviour effectively?

A: Strategies for testing polymorphic code:
1. Write tests against the supertype/interface reference — test the contract, not the implementation.
2. Use parameterised tests to run the same test suite against each concrete implementation (e.g., JUnit 5 @MethodSource).
3. Test each subclass in isolation for its specific behaviour.
4. Use mock/spy frameworks (Mockito) to verify that the correct overridden method is called.
5. For LSP compliance, verify that all parent-class test cases pass for each subclass (Liskov test suites).

Q109. What is a functional interface and how does it relate to polymorphism?

A: A functional interface has exactly one abstract method (SAM — Single Abstract Method). Examples: Runnable, Callable, Comparator, Function, Predicate. Lambda expressions and method references are a form of polymorphism — they implement the functional interface's single method. This is a powerful and concise form of runtime polymorphism without explicit subclassing: Comparator<String> c = String::compareToIgnoreCase;

Q110. Summarise the key pitfalls of Java inheritance to watch for in interviews.

A: The top pitfalls:
1. Calling overridden methods from constructors — subclass fields not yet initialised.
2. Breaking equals/hashCode contract when adding fields in subclasses.
3. Forgetting to override hashCode when overriding equals — breaks hash collections.
4. Violating LSP — subclass that cannot safely substitute parent (Square-Rectangle problem).
5. Fragile base class — changes to parent silently break subclasses.
6. Diamond problem with default method conflicts — compile error if not resolved.
7. Array covariance + generics invariance mismatch — ArrayStoreException at runtime.
8. Static method hiding confused with overriding — wrong dispatch via reference type.
9. Over-deep inheritance hierarchies — makes code hard to reason about; prefer flat hierarchies with composition.
10. Sealed class missing non-sealed / final modifier on permitted subclass — compile error.

No comments
Leave a Comment