Welcome to the Java Method Overriding MCQ Quiz! In this quiz, we have compiled 30 important multiple-choice questions (MCQs) on method overriding in Java to help you strengthen your understanding of this essential object-oriented programming (OOP) concept.
Whether you are a beginner, a student, or a candidate preparing for technical interviews, these questions will help you test your knowledge and improve your Java programming skills.
The quiz covers key concepts of method overriding, along with practical examples and commonly asked interview questions. So, let’s get started and see how well you understand method overriding in Java!
class Parent {
public void display() {
System.out.println("Parent");
}
}
class Child extends Parent {
// Attempting to override with tighter access
private void display() {
System.out.println("Child");
}
}- Visibility Rule: Java enforces that an overriding method cannot have a lower access privilege than the method it overrides in the superclass.
- Access Hierarchy: From most restrictive to least restrictive, access levels are private, default, protected, and public.
- Compilation Failure: Since the parent method is public, reducing the child method’s visibility to private violates this rule and triggers a compile-time error.
- Polymorphism Guarantee: Maintaining or expanding accessibility ensures that any code capable of calling the superclass method can safely invoke the overridden implementation through polymorphism.
import java.io.IOException;
class Reader {
void readData() throws IOException {
throw new IOException("IO Error");
}
}
class FileReader extends Reader {
@Override
void readData() throws Exception {
System.out.println("Reading data");
}
}
public class Main {
public static void main(String[] args) {
Reader r = new FileReader();
try {
r.readData();
} catch (IOException e) {
System.out.println("Caught IOException");
}
}
}- Checked Exception Rule: An overriding method can throw the same exception, a subclass exception (narrower), or no exception at all, but it cannot throw a broader or new checked exception.
- Exception Hierarchy:
Exceptionis a broader checked exception thanIOException. - Polymorphic Safety: This restriction ensures that callers handling superclass exceptions are not surprised by unexpected broader checked exceptions at runtime.
- Result: The code fails to compile due to incompatible throws clauses in the method override.
final in its superclass?class Base {
final void display() {
System.out.println("Base final method");
}
}
class Derived extends Base {
@Override
void display() {
System.out.println("Derived method");
}
}
public class Main {
public static void main(String[] args) {
Base obj = new Derived();
obj.display();
}
}- Final Modifier: The
finalkeyword applied to a method prevents any subclass from overriding or modifying its implementation. - Design Intent: Developers use
finalto enforce consistent behavior and secure critical business logic from unintended alterations in inheritance hierarchies. - Compilation Result: The Java compiler detects the override attempt on a
finalmethod and immediately triggers a compilation error.
class Parent {
private void show() {
System.out.println("Parent private");
}
public void display() {
show();
}
}
class Child extends Parent {
// This is method hiding/shadowing, not overriding
private void show() {
System.out.println("Child private");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
obj.display();
}
}- Private Visibility: Private methods are strictly confined to their defining class and are not inherited by subclasses. Therefore, subclasses cannot override them.
- Method Shadowing: Declaring a method with the same signature in the subclass defines a completely separate, unrelated method unique to the child class rather than overriding the parent’s method.
- Execution Flow: When
obj.display()is called, it executes thedisplay()method defined inParent. WithinParent, theshow()method invoked is bound toParent‘s privateshow()method. - Output: The program runs successfully and prints “Parent private”.
class Shape {
Shape draw() {
System.out.println("Drawing Shape");
return this;
}
}
class Circle extends Shape {
@Override
Circle draw() {
System.out.println("Drawing Circle");
return this;
}
}
public class Main {
public static void main(String[] args) {
Shape s = new Circle();
s.draw();
}
}- Covariant Return Types: Java allows an overriding method to return a subtype of the return type declared in the superclass method. Since
Circleis a subtype ofShape, this override is fully valid. - Dynamic Method Dispatch: At runtime, the reference variable
spoints to an instance ofCircle. Polymorphism ensures that the overridden method in the actual object type is executed. - Execution Result: The method invocation resolves to
Circle.draw(), printing “Drawing Circle” to the console.
class Parent {
void process(int x) { /* ... */ }
}
class Child extends Parent {
// Must follow overriding rules
}- Method Signature Requirement: True method overriding requires the overriding method in the child class to have an identical name and parameter list (signature) to the method in the parent class. If parameters differ, it results in method overloading instead of overriding.
- Return Type Flexibility: While parameter types and order must match precisely, Java permits covariant return types where the return type can be a subtype.
- Access and Exceptions: Overriding methods cannot reduce access visibility or throw broader/new checked exceptions compared to the parent method.
- Core Concept: Maintaining identical signatures ensures that runtime polymorphism can correctly dispatch calls based on the actual object instance rather than the reference type.
class Super {
int value = 10;
int getValue() {
return value;
}
}
class Sub extends Super {
int value = 20;
@Override
int getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
Super obj = new Sub();
System.out.println(obj.value + " " + obj.getValue());
}
}- Field Hiding: Instance variables in Java are hidden rather than overridden. Accessing a field via a reference variable (
obj.value) is resolved at compile time based on the declared reference type (Super), evaluating to10. - Method Overriding and Dynamic Dispatch: Methods participate in runtime polymorphism. Calling
obj.getValue()dispatches dynamically to the overridden implementation in the actual object type (Sub), which returnsSub‘svaluefield (20). - Combined Output: The expression evaluates to printing
10 20to the console.
class Super {
Super() {
callMe();
}
void callMe() {
System.out.println("Super.callMe()");
}
}
class Sub extends Super {
int x = 42;
@Override
void callMe() {
System.out.println("Sub.callMe() x = " + x);
}
}
public class Main {
public static void main(String[] args) {
Sub s = new Sub();
}
}- Constructor Execution Order: When instantiating a subclass object, the superclass constructor executes before the subclass instance fields are initialized.
- Polymorphism in Constructors: Dynamic method dispatch still applies during constructor execution, so calling
callMe()invokes the overridden version inSubrather thanSuper. - Uninitialized State: At the moment the superclass constructor calls
callMe(),Sub‘s instance variablexhas not yet been assigned its value of42and retains its default primitive value of0. - Best Practice Warning: Calling overridable methods from within constructors is a classic Java pitfall that leads to subtle bugs due to partially initialized state.
class Processor<T> {
public T execute(T input) {
return input;
}
}
class StringProcessor extends Processor<String> {
@Override
public String execute(String input) {
return input.toUpperCase();
}
}
public class Main {
public static void main(String[] args) {
Processor<Object> p = new StringProcessor();
System.out.println(p.execute("test"));
}
}- Type Erasure: During compilation, generic type parameters like
Tare erased and replaced with their bounds (orObject), meaning the superclass method signature in bytecode becomesObject execute(Object input). - Signature Mismatch: The subclass overrides the method with a specific type (
String execute(String input)), which creates a distinct method signature in the compiled bytecode that would otherwise break polymorphic dispatch when referenced via a superclass type. - Bridge Method Generation: To resolve this discrepancy, the Java compiler automatically generates a synthetic “bridge method” in the subclass with the erased signature (accepting and returning
Object) that delegates the call to the type-safe method. - Polymorphism Guarantee: This compiler-generated mechanism ensures that dynamic method dispatch works seamlessly across generic type boundaries without causing
AbstractMethodErroror runtime class mismatches.
synchronized modifier in Java method overriding. Which of the following statements is true regarding whether a subclass method inherits or must retain the synchronized modifier from its superclass?class Base {
synchronized void process() {
System.out.println("Base process");
}
}
class Derived extends Base {
@Override
void process() {
System.out.println("Derived process");
}
}
public class Main {
public static void main(String[] args) {
Base obj = new Derived();
obj.process();
}
}- Signature Independence: Modifiers like
synchronized,native, andstrictfpare implementation details and are not part of the method signature. - Inheritance Rule: Subclass methods do not automatically inherit these execution modifiers from the overridden superclass method.
- Thread Safety Impact: Omitting
synchronizedin the subclass removes intrinsic object-level locking for that specific override, which can lead to race conditions if thread safety is required. - Compilation Behavior: The program compiles successfully regardless of whether the subclass retains the modifier.
interface InterfaceA {
default void display() {
System.out.println("Interface A");
}
}
interface InterfaceB {
default void display() {
System.out.println("Interface B");
}
}
class MyClass implements InterfaceA, InterfaceB {
// Missing explicit override of display()
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display();
}
}- Diamond Problem in Interfaces: When a class inherits multiple default methods with identical signatures from unrelated interfaces, Java considers it an ambiguous conflict.
- Compiler Enforcement: To prevent unpredictable behavior, the Java compiler mandates that the implementing class must explicitly override the conflicting method.
- Resolution Syntax: The developer can resolve the conflict by overriding the method and explicitly invoking the desired version using the syntax
InterfaceName.super.methodName().
class Animal {}
class Dog extends Animal {}
class Shelter {
Animal[] getAnimals() {
return new Animal[0];
}
}
class DogShelter extends Shelter {
@Override
Dog[] getAnimals() {
return new Dog[0];
}
}
public class Main {
public static void main(String[] args) {
Shelter s = new DogShelter();
System.out.println(s.getAnimals().getClass().getSimpleName());
}
}- Array Covariance: In Java, array types are covariant, meaning if
Dogis a subtype ofAnimal, thenDog[]is considered a subtype ofAnimal[]. - Covariant Return Types: Since Java 5, an overriding method is permitted to return a subtype of the return type declared in the superclass method. Because
Dog[]is a valid subtype ofAnimal[], this override compiles successfully. - Dynamic Method Dispatch: At runtime, the reference variable
spoints to aDogShelterinstance. Polymorphism executesDogShelter.getAnimals(), returning aDog[]array. - Output: Invoking
getClass().getSimpleName()on the returned array object accurately reflects its runtime type asDog[].
class Vehicle {
void start() {
System.out.println("Vehicle starts");
}
}
class Car extends Vehicle {
@Override
void start() {
System.out.println("Car starts with key");
}
}
public class Main {
public static void main(String[] args) {
Vehicle myVehicle = new Car();
myVehicle.start();
}
}- Runtime Polymorphism: Although the reference variable
myVehicleis of typeVehicle, it holds an instance of the subclassCar. - Method Overriding: The
start()method is overridden inCar, replacing the superclass behavior. - Dynamic Dispatch: Java resolves overridden method calls based on the actual object type at runtime, executing
Car‘s version. - Output: The program prints “Car starts with key”.
class Parent {
int getValue() {
return 10;
}
}
class Child extends Parent {
@Override
String getValue() {
return "10";
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
System.out.println(obj.getValue());
}
}- Return Type Rule: An overriding method must specify a return type that is identical to or a covariant subtype of the return type declared in the superclass method.
- Incompatibility:
Stringis completely unrelated to primitiveintin the type hierarchy. - Compilation Failure: The Java compiler detects this clash during compilation and generates an error indicating that methods cannot differ solely by return type.
static method?class Parent {
void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
@Override
static void show() {
System.out.println("Child show");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
obj.show();
}
}- Instance vs Static Conflict: An instance method relies on dynamic dispatch and runtime object types, whereas a static method is bound to the class at compile time.
- Overriding Rule: Java explicitly prohibits changing an instance method into a static method (or vice versa) during overriding because it breaks polymorphic behavior.
- Compilation Failure: The compiler flags this modifier incompatibility as an error, preventing the code from building.
class Parent {
int getValue() {
return 10;
}
}
class Child extends Parent {
@Override
Integer getValue() {
return 20;
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
System.out.println(obj.getValue());
}
}- Primitive Return Types: Primitive types (like
int) and their wrapper classes (likeInteger) are distinct types in Java’s type system; wrappers are not considered subclasses of primitives. - Covariant Rule: Covariant return types only apply to reference types where the return type is an actual subclass or subtype.
- Compilation Result: Because the return type signature does not match or qualify as a valid reference subtype, the compiler reports an error.
equals() method in a subclass. What is the output or behavior of this program?class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
String color;
ColoredPoint(int x, int y, String color) {
super(x, y);
this.color = color;
}
// Intended to override equals()
public boolean equals(ColoredPoint other) {
if (other == null) return false;
return this.x == other.x && this.y == other.y && this.color.equals(other.color);
}
}
public class Main {
public static void main(String[] args) {
Point p1 = new ColoredPoint(1, 2, "Red");
Point p2 = new ColoredPoint(1, 2, "Red");
System.out.println(p1.equals(p2));
}
}- Method Signature Mismatch: The
equals()method must take anObjectparameter to correctly overrideObject.equals(). TakingColoredPointcreates an overloaded method instead of an override. - Reference Type Resolution: Because variable
p1is declared as typePoint, the program invokes theequals(Object)method contract. - Fallback Behavior: Since
PointinheritsObject.equals(), it performs reference comparison using==, returningfalsefor distinct object instances despite identical field values.
class Parent {
void process() {
System.out.println("Parent process");
}
}
class Child extends Parent {
@Override
void process() throws NullPointerException, ArithmeticException {
System.out.println("Child process");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
obj.process();
}
}- Unchecked Exceptions Rule: The restrictions on throwing checked exceptions in overridden methods do not apply to unchecked exceptions (subclasses of
RuntimeException). - Throws Declaration: An overriding method is legally permitted to declare any unchecked exceptions in its
throwsclause, even if the parent method does not declare them. - Compilation & Execution: Because
NullPointerExceptionandArithmeticExceptionare runtime exceptions, the code compiles cleanly and executes successfully, printing “Child process”.
import java.io.IOException;
import java.io.FileNotFoundException;
class Reader {
void readData() throws IOException {
throw new IOException("General IO Exception");
}
}
class FileReader extends Reader {
@Override
void readData() throws FileNotFoundException {
throw new FileNotFoundException("File not found");
}
}
public class Main {
public static void main(String[] args) {
Reader obj = new FileReader();
try {
obj.readData();
} catch (IOException e) {
System.out.println("Caught IO Exception");
}
}
}- Checked Exception Overriding Rule: An overriding method is permitted to throw narrower checked exceptions (subclasses) or no checked exceptions at all compared to the superclass method.
- Exception Hierarchy:
FileNotFoundExceptionis a subclass ofIOException, making it a narrower, valid exception declaration. - Polymorphic Safety: Because any subclass exception can be caught by a catch block handling the superclass exception type, safety is fully preserved.
- Execution Result: The program compiles cleanly, executes via dynamic dispatch, and prints “Caught IO Exception”.
class Parent {
void process(String... items) {
System.out.println("Parent varargs");
}
}
class Child extends Parent {
@Override
void process(String[] items) {
System.out.println("Child array");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
obj.process("A", "B");
}
}- Varargs Representation: In Java bytecode, a varargs parameter (e.g.,
String...) is compiled as a single-dimension array (e.g.,String[]). - Signature Compatibility: Because both declarations resolve to the exact same parameter type signature at the bytecode level, an explicit array parameter legally overrides a varargs parameter.
- Execution Result: The code compiles cleanly and executes successfully via dynamic method dispatch.
class A {
void show() {
System.out.println("A");
}
}
class B extends A {
@Override
void show() {
System.out.println("B");
}
}
class C extends B {
// Inherits show() from B without overriding
}
public class Main {
public static void main(String[] args) {
A obj = new C();
obj.show();
}
}- Inheritance of Overridden Methods: Subclasses automatically inherit methods that were overridden by their superclasses. Class
Cinherits the overriddenshow()method fromB. - Dynamic Method Dispatch: At runtime, Java resolves method calls by searching up the inheritance hierarchy starting from the actual object type (
C) to find the most specific implementation, which is inB. - Output: The program executes successfully and prints “B”.
// File in package: pkg1
package pkg1;
public class Parent {
void display() {
System.out.println("Parent");
}
}
// File in package: pkg2 (different package)
package pkg2;
import pkg1.Parent;
public class Child extends Parent {
@Override
public void display() {
System.out.println("Child");
}
}- Package-Private Visibility: Package-private (default) members are strictly restricted to classes within the exact same package and are neither inherited nor visible across package boundaries.
- Overriding Prerequisite: For a method to be overridden, it must first be visible to the subclass. Because
Childinpkg2cannot seeParent‘s package-privatedisplay()method, it cannot override it. - Annotation Enforcement: The presence of the
@Overrideannotation instructs the compiler to verify that a superclass method is actively being overridden. Since no visible superclass method matches, the compiler triggers an error.
class SuperClass {
public void display() {
System.out.println("SuperClass method");
}
}
interface MyInterface {
default void display() {
System.out.println("Interface default method");
}
}
class Child extends SuperClass implements MyInterface {
// No explicit override of display()
}
public class Main {
public static void main(String[] args) {
Child obj = new Child();
obj.display();
}
}- Class Wins Rule: When a class inherits a method with the same signature from both a superclass and an interface default method, the superclass method automatically takes precedence.
- No Ambiguity Conflict: Unlike multiple interface inheritance where competing default methods cause a compilation error, Java resolves class-versus-interface method collisions deterministically in favor of the class.
- Output: The program compiles cleanly and prints “SuperClass method”.
class Base {
void process(int x) {
System.out.println("Base int");
}
}
class Derived extends Base {
void process(Integer x) {
System.out.println("Derived Integer");
}
}
public class Main {
public static void main(String[] args) {
Base obj = new Derived();
obj.process(10);
}
}- Overloading vs. Overriding: Changing a parameter type from primitive
intto wrapperIntegercreates an overloaded method rather than overriding the superclass method. - Inheritance: Class
Derivedinheritsprocess(int)fromBasewhile introducing a separate overloaded methodprocess(Integer). - Dispatch Behavior: Because the reference variable
objis of typeBase, the compiler and runtime invoke the inheritedprocess(int)method, printing “Base int”.
private method in its superclass. What is the result when attempting to invoke this method via a superclass reference?class Parent {
private void display() {
System.out.println("Parent private");
}
}
class Child extends Parent {
public void display() {
System.out.println("Child public");
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
p.display();
}
}- Private Visibility:
privatemembers are strictly confined to their defining class and are neither inherited nor visible to subclasses. - Independent Methods: The method in
Childdoes not overrideParent‘s method; instead, it is an entirely independent, new method. - Compile-Time Check: When invoking a method on a reference of type
Parent, the compiler checks visibility withinParent. Becausedisplay()is private inParent, the compiler rejects the call with an accessibility error.
abstract class PaymentProcessor {
abstract void processPayment(double amount);
}
class StripeProcessor extends PaymentProcessor {
@Override
void processPayment(double amount) {
System.out.println("Processing $" + amount + " securely via Stripe API.");
}
}
class PayPalProcessor extends PaymentProcessor {
@Override
void processPayment(double amount) {
System.out.println("Processing $" + amount + " securely via PayPal Sandbox.");
}
}
public class Main {
public static void executeTransaction(PaymentProcessor processor, double amount) {
processor.processPayment(amount);
}
public static void main(String[] args) {
PaymentProcessor gateway = new StripeProcessor();
executeTransaction(gateway, 150.00);
}
}- Polymorphism in Architecture: Real-world payment systems use polymorphic references (
PaymentProcessor) so that high-level checkout services can execute transactions without coupling to specific vendor implementations. - Dynamic Dispatch: At runtime, Java inspects the actual object reference assigned (
StripeProcessor) and routes the method invocation to the correct implementation. - Extensibility: New payment vendors can be integrated seamlessly into the codebase by extending the base class and overriding
processPayment()without rewriting existing transaction logic.
class BaseRepository {
public Object findById(long id) {
return "Generic Record";
}
}
class UserRepository extends BaseRepository {
@Override
public String findById(long id) {
return "User Profile [ID: " + id + "]";
}
}
public class Main {
public static void main(String[] args) {
BaseRepository repo = new UserRepository();
System.out.println(repo.findById(101));
}
}- Covariant Return Types: An overriding method is permitted to specify a return type that is a subtype (such as
String) of the return type declared in the superclass (such asObject). - Dynamic Method Dispatch: At runtime, polymorphism resolves the method call against the actual object instance type (
UserRepository), executing the specialized implementation. - Real-World Utility: This pattern allows repository layers to return specific domain models or DTOs directly without requiring callers to manually cast generic return types.
import java.io.IOException;
import java.sql.SQLException;
class ConfigurationLoader {
public void loadConfig() throws IOException {
// Base config loading logic
}
}
class XMLConfigurationLoader extends ConfigurationLoader {
@Override
public void loadConfig() throws SQLException, IOException {
// XML specific loading logic
}
}- Checked Exception Overriding Rule: An overriding method cannot throw broader or new checked exceptions that are not subclasses of the exceptions declared in the superclass method.
- Compilation Failure: Because
SQLExceptionis unrelated to and broader than the exception contract established byConfigurationLoader.loadConfig(), the compiler flags this as an error to preserve caller type safety.
interface Renderable {
default void render() {
System.out.println("Rendering Renderable component");
}
}
interface Visual {
default void render() {
System.out.println("Rendering Visual component");
}
}
class Panel implements Renderable, Visual {
// Missing explicit override implementation
}- Diamond / Multiple Inheritance Conflict: When a class inherits two or more default methods with the exact same signature from unrelated interfaces, Java considers it an ambiguous inheritance conflict.
- Explicit Resolution Required: The compiler refuses to guess which default implementation to prefer and halts with an error unless the implementing class explicitly resolves the conflict by overriding the method and choosing one (e.g.,
Renderable.super.render()).
interface DataProcessor<T> {
T processData(String input);
}
class IntegerDataProcessor implements DataProcessor<Integer> {
@Override
public String processData(String input) {
return input.length() + "";
}
}- Generic Type Substitution: When
IntegerDataProcessorimplementsDataProcessor<Integer>, all occurrences of type parameterTinside the interface are replaced withInteger. Therefore, the required method signature becomesInteger processData(String input). - Compilation Failure: Because the implementing class defines
processData()to return aStringinstead of anInteger, it fails to fulfill the interface contract, triggering a compilation error.




