Java Inheritance Interview Questions

Reviewed by Mark Dickie · Last updated

3 questions on this page you can answer and see marked on the spot. Go to the first one

Java inheritance is a mechanism where a class acquires fields and methods from another class through the extends keyword. A subclass reuses, overrides, or adds to the behavior of its parent (the superclass), and every class implicitly extends java.lang.Object when no other parent is named. For interviews, you need a working grasp of single and multilevel inheritance, the super keyword, constructor chaining, method overriding versus overloading, and why Java forbids multiple class inheritance.

What does a Java inheritance interview test?

Interviewers check whether you can model class hierarchies correctly and reason about what the code does at runtime. The areas that come up most:

ConceptWhat gets asked
Single inheritanceOne subclass extends one superclass
Multilevel inheritanceA chain such as C extends B, B extends A
super keywordCalling parent constructors and overridden methods
Method overridingSame name and signature in subclass, runtime dispatch
Constructor chainingParent constructor body runs before child body
final and sealed classesPreventing further inheritance

Common inheritance interview questions

  1. What is the difference between extends and implements?
  2. Can a subclass reduce the visibility of an overridden method? (No, you can widen access but not narrow it.)
  3. What happens if a superclass constructor throws a checked exception?
  4. How do sealed classes and permits work in Java 17 and later?
  5. What is the output when both parent and child define static methods with the same name?

Static methods are not inherited the way instance methods are. They resolve at compile time based on the reference type, not the object type, and this trips up many candidates.

How should you prepare for inheritance questions?

Run small code snippets by hand and predict the output before you compile. Pay attention to constructor call order, the default no-arg constructor the compiler inserts, and what breaks when a parent class has no no-arg constructor. Practice writing hierarchies where overriding and super calls interact, since interviewers like to nest those scenarios two or three layers deep.

Key facts

  • Tarmac's Java interview questions cover 15 questions at difficulty 1–5 of 5.
  • Tarmac tracked 1,645 job postings asking for Java in September 2026.
  • Roles asking for Java advertise a median base salary of £80,000, across 352 job postings as of September 2026.
  • Tarmac last reviewed these Java interview questions on 14 October 2026.

At a glance

Questions15
Difficulty1–5 of 5
FormatsTrue / false, Short answer, Fill in the blank, Code output, Flashcard, Ordering, Find the bug

What you'll review

  1. inheritance

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

In Java, a class may directly extend more than one parent class. True or false?#

Options

Show answer

A Java class cannot directly extend more than one parent class. Java supports only single inheritance for classes, so a class declaration uses extends with exactly one superclass. This avoids the diamond problem, where resolving an inherited member becomes ambiguous. A class may still implement multiple interfaces.

Why:

Java allows only single inheritance for classes. A class declaration uses extends with exactly one superclass, so a class cannot directly extend more than one parent. The language designers excluded multiple class inheritance to avoid the diamond problem, where a member could be inherited ambiguously from two parents. A class may implement multiple interfaces, but it cannot extend more than one class, so the statement is false.

In Java, what keyword is used to declare that a class inherits from exactly one superclass?#

Show answer

The extends keyword. A class declared as class Child extends Parent inherits from exactly one superclass; Java does not allow a class to extend more than one class.

Why:

Java supports single class inheritance only. A class uses the extends keyword to inherit from at most one superclass; attempting to extend two classes is a compile-time error. Multiple inheritance of type is available through implements, but not of classes. The question asks for a single keyword with one defensible answer, so it stays an open short-answer question graded by the keyword extends.

In Java, a subclass can call the constructor of its parent class by using the _____ keyword as the first statement inside the subclass constructor.#

Show answer

In Java, a subclass can call the constructor of its parent class by using the super keyword as the first statement inside the subclass constructor.

Why:

In Java, the super keyword is used to invoke a parent class constructor. The call super(...) must be the first statement in the subclass constructor; if omitted, the compiler inserts a no-arg super() call automatically.

In Java, what rule allows an overriding method to return a subtype of the return type declared by the overridden method, and since which Java version has this been legal?#

Show answer

Covariant return types: an overriding method may declare a return type that is a subtype of the overridden method's declared return type. This has been legal since Java 5 (J2SE 5.0).

Why:

Since Java 5, an overriding method may declare a return type that is a subtype of the return type of the method it overrides, which is called a covariant return type. Before Java 5, the return type had to match exactly. The parameter list must still match exactly for the method to be an override, and changing only the return type without a subtype relationship causes a compile error.

What is the exact output of the following Java program? Consider the order of field initialization, constructor chaining, and dynamic dispatch rules.#

class Parent {
    String name = "Parent";

    Parent() {
        describe();
    }

    void describe() {
        System.out.println("Parent: " + name);
    }
}

class Child extends Parent {
    String name = "Child";

    @Override
    void describe() {
        System.out.println("Child: " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        new Child();
    }
}
Show answer
Child: null
Why:

When new Child() runs, Child's constructor first makes an implicit super() call. Parent's field initializers run before Parent's constructor body, so Parent's name is set to Parent, and then the Parent constructor calls describe(). Because Java dispatches instance methods dynamically on the runtime type, the overridden describe() in Child runs. That method reads Child's own name field, which hides Parent's field, but Child's field initializers have not run yet because they execute only after the super constructor returns. So Child's name is still null, and the concatenation prints Child: null.

In big-O notation, what is the amortized time complexity of a single append (push to the end) operation on a dynamic array (such as a Python list or C++ std::vector) that doubles its underlying capacity when it runs out of space? Give the bound and briefly justify it.#

Show answer

Amortized O(1). Most appends write into spare capacity in constant time. The occasional resize copies up to n elements, but because capacity doubles, the total copying over n appends is bounded by a geometric series of at most about 2n, so the average cost per append is constant.

Why:

Individual appends are usually O(1), but an append that triggers a resize copies up to n elements, so a single operation can cost O(n). Because capacity doubles, resizes occur at sizes 1, 2, 4, 8, and so on, so the copies over n appends form a geometric series summing to at most about 2n. Dividing that total O(n) by n appends gives O(1) amortized cost per append. The correct answer therefore names the amortized qualifier together with the constant O(1) bound.

In Java, explain why calling an overridable instance method (one that is neither final nor private) from a superclass constructor can produce surprising results when a subclass overrides that method. Describe the order of construction and what state the overriding method sees.#

Show answer

When a subclass instance is created, the superclass constructor runs before the subclass constructor body and before the subclass's field initializers run. If the superclass constructor calls an overridable instance method, dynamic dispatch selects the subclass override, because the object's runtime type is already the subclass. That override then reads subclass fields that have not been initialized yet, so they still hold their default values (0, false, or null), which can yield wrong results or a NullPointerException. The override may also depend on invariants that the subclass constructor has not established yet. The standard remediation (Effective Java, Item 19, 'Design and document for inheritance or else prohibit it') is to avoid calling overridable methods from constructors: declare the method final or private, or compute the value lazily outside the constructor.

Why:

The question tests object construction order in Java inheritance. The superclass constructor executes before the subclass constructor body and before subclass field initializers, so the object exists in a partially constructed state. Because dispatch on an instance method uses the runtime type, a call from the superclass constructor reaches the subclass override, which then sees subclass fields at their default values (0, false, null) rather than their intended initial values. This is why the surprising behavior appears only when the class is subclassed and the method is overridable. The remedy is to make the method final or private, or to avoid calling it during construction, which removes the dynamic dispatch hazard.

What is printed when main runs?#

class Base { String tag = Base; String getTag() { return tag; } } class Sub extends Base { String tag = Sub; } public class Main { public static void main(String[] args) { Base b = new Sub(); System.out.println(b.tag +  + b.getTag()); } }
Show answer
Base Base
Why:

Field access in Java is resolved by the declared (compile-time) type, not by dynamic dispatch. b.tag uses the compile-time type Base, so it reads the Base field Base. Method calls are virtual: b.getTag() dispatches to Base.getTag() because Sub does not override it, returning Base.tag which is Base. Output: Base Base.

In Java inheritance, what is the difference between static method hiding and instance method overriding with respect to dynamic dispatch?#

Show answer

Instance method overriding uses dynamic dispatch: at runtime, the JVM selects the actual implementation based on the runtime type of the object, so a subclass override is invoked even through a superclass-typed reference. Static method binding, by contrast, is resolved at compile time based on the declared type of the reference. When a subclass defines a static method with the same signature as a superclass static method, it is said to hide (not override) the superclass method. Calling a hidden static method through a superclass reference invokes the superclass version, not the subclass version — there is no runtime polymorphism for static methods.

Why:

Must cover dynamic dispatch for instance methods (runtime type) vs compile-time binding for static method hiding (declared type), and the consequence that hidden static methods are not polymorphic. A correct answer contrasts runtime dispatch on the object's actual class for overridden instance methods with compile-time resolution on the reference's declared type for hidden static methods, and notes that calling a hidden static method through a superclass reference runs the superclass version.

When is it safe to call an overridable instance method from a Java constructor?#

Show answer

Never: invoking an overridable method from a constructor dispatches to the subclass override before the subclass constructor body has run, so subclass fields are still at default values and invariants are not yet established. Mark the method final or private, or compute lazily outside construction (Effective Java Item 9).

Why:

The flashcard tests knowledge of the classic pitfall: overridable method calls in constructors run subclass code with uninitialized state.

In Java, Sub extends Base. Base's constructor calls init(), and Sub overrides init(). Put these events from executing new Sub() into the order they occur, first to last:#

Put these in order

Show answer

In Java, when new Sub() runs, instance fields are first set to defaults, then Base's field initializers run, then Base's constructor body runs. Next come Sub's field initializers and finally Sub's constructor body. Because Base's constructor body runs before Sub's field initializers, an overridden init() called from it sees Sub's fields still at default values.

Why:

Java allocates the object and zeroes every instance field (Base's and Sub's) before any constructor runs, so the defaults step is first. The implicit super() call then enters the Base constructor, which runs Base's field initializers before its own body. Inside that body, the call to init() dispatches virtually to Sub.init() because the object is a Sub, but Sub's field initializers have not run yet, so Sub's fields still hold default values. Only after the Base constructor returns does the compiler-generated code run Sub's field initializers, and the Sub constructor body comes last. The key non-obvious point is that the Base constructor body precedes the Sub field initializers.

What exact line does the following Java program print when executed? Consider how field access on a reference of type Base differs from instance method dispatch in Java inheritance.#

class Base {
    String tag = "base-field";
    String resolve() { return tag; }
    static String label() { return "base"; }
}

class Derived extends Base {
    String tag = "sub-field";
    @Override
    String resolve() { return tag; }
    static String label() { return "derived"; }
}

public class Main {
    public static void main(String[] args) {
        Base obj = new Derived();
        System.out.println(obj.tag);
    }
}
Show answer
base-field
Why:

The variable obj has compile-time type Base but refers to a Derived object. Instance fields are resolved by the declared type of the reference, so obj.tag reads Base.tag, whose value is base-field. Derived declares its own tag field with value sub-field, but that field only hides the superclass field and is not reached through a Base reference. Had the program called obj.resolve() instead, dynamic dispatch would run Derived.resolve() and print sub-field, but the program only prints the field, so the output is base-field.

The Java code below is meant to compute a result by delegating part of the computation to the parent class's apply method and then adding the subclass's threshold. Instead, calling new FilteredProcessor(5).apply(30) throws a StackOverflowError at runtime. Identify the 1-based line number of the buggy line.#

class Processor {
    int apply(int value) {
        return value * 2;
    }
}

class FilteredProcessor extends Processor {
    private final int threshold;

    FilteredProcessor(int threshold) {
        this.threshold = threshold;
    }

    @Override
    int apply(int value) {
        // intended: delegate to the parent's apply, then add threshold
        return apply(value - 10) + threshold;
    }
}
Show answer

The bug is on line 17.

Why:

Line 17 calls apply(value - 10) without a super qualifier. The implicit receiver is this, a FilteredProcessor, so dynamic dispatch selects FilteredProcessor.apply(), the method that is already executing. That method has no base case, so every call makes another call with no end and the stack overflows with a StackOverflowError. The fix is return super.apply(value - 10) + threshold;, which bypasses dynamic dispatch and invokes Processor.apply() directly, so the recursion stops after one level.

What is the exact text printed to standard output when the following Java program is run with java Main? Include the exact characters printed, or state that nothing is printed.#

class Base {
    static String id() { return "Base"; }
    String name() { return "Base"; }
    void show() { System.out.println(id() + ":" + name()); }
}

class Derived extends Base {
    static String id() { return "Derived"; }
    @Override
    String name() { return "Derived"; }
}

public class Main {
    public static void main(String[] args) {
        Base b = new Derived();
        b.show();
    }
}
Show answer
Base:Derived
Why:

The variable b has static type Base but refers to a Derived instance. Derived does not override show(), so b.show() runs Base.show(). Inside that method, id() is a static method, so it is bound at compile time to the id() declared in the enclosing class Base, which returns "Base". (The static String id() in Derived only hides it and is not reached.) The call name() is an instance method, so it is dispatched at runtime on the actual object, a Derived, and Derived.name() returns "Derived". The println therefore prints Base:Derived.

Every context-free language — that is, every language accepted by some nondeterministic pushdown automaton — can also be accepted by a deterministic pushdown automaton.#

Options

Show answer

False — not every context-free language can be accepted by a deterministic pushdown automaton. The deterministic context-free languages form a strict proper subset of all context-free languages; for example, the palindromic language { w w^R : w ∈ {a,b}* } is context-free but not deterministic context-free.

Why:

This is false. The class of deterministic context-free languages (DCFLs) is a strict proper subset of the class of all context-free languages (CFLs). Every DCFL is context-free, but not every CFL is deterministic. A classic counterexample is the language of palindromes over a two-letter alphabet, L = { w w^R : w ∈ {a,b}* }, which is generated by the grammar S → aSa | bSb | ε and is therefore context-free, yet cannot be recognized by any deterministic pushdown automaton because determining the midpoint of the input requires unbounded nondeterministic guessing. The two classes also differ in closure properties: DCFLs are closed under complement but not under union, while CFLs are closed under union but not under complement.

Related interview questions

Job market

See java salaries and hiring demand from live job postings.

Practise these until they stick

That's every question we hold on this topic, and the page marks what you pick. What it can't do is remember. A free account keeps every answer, and what you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.