Welcome to this comprehensive Java Method Overloading MCQ quiz, designed to test and elevate your understanding of compile-time polymorphism in Java.
Whether you are preparing for a rigorous technical interview, certifying your skills, or sharpening your coding expertise, this collection of multiple-choice questions provides deep insights backed by detailed explanations.
Method overloading is a core pillar of object-oriented programming, but the basics are not enough. We have compiled the best collection of 30 challenging multiple-choice questions that go far beyond textbook definitions to challenge developers at any skill level.
Let’s dive in and test your knowledge of method overloading in Java!
public class Calculator {
public int add(int a, int b) {
return a + b;
}
// Which rule allows another add method here?
}Method overloading in Java allows a class to have multiple methods with the same name as long as their parameter lists are distinct.
- The method name must remain identical.
- The parameter lists must differ by data type, number of parameters, or the order of parameters.
- Changing only the return type does not constitute method overloading and results in a compile-time error.
public class OverloadTest {
public void display(int a) {
System.out.println("int version");
}
public void display(long a) {
System.out.println("long version");
}
public static void main(String[] args) {
OverloadTest obj = new OverloadTest();
obj.display(5);
}
}During compile-time method resolution, the Java compiler prioritizes matching types before considering wider types.
- The argument literal
5is anintby default. - Because a method with an exact
intparameter exists, the compiler selects it immediately. - Widening conversion (from
inttolong) is only utilized if an exact match cannot be found.
public class Printer {
public void print(String text, int count) {
System.out.println("String-int");
}
public void print(int count, String text) {
System.out.println("int-string");
}
public static void main(String[] args) {
Printer p = new Printer();
p.print(10, "Hello");
}
}Method overloading allows methods with the same name as long as their parameter types, order, or number are distinct.
- The first method expects a
Stringfollowed by anint. - The second method expects an
intfollowed by aString. - When calling
p.print(10, "Hello"), the arguments match anintand aStringin that exact order, so the compiler selects the second method.
public class ResolutionTest {
public static void process(long l) {
System.out.println("Primitive Widening");
}
public static void process(Integer i) {
System.out.println("Boxing");
}
public static void main(String[] args) {
int val = 10;
process(val);
}
}According to the Java Language Specification (JLS), primitive widening conversion takes precedence over boxing conversion during compile-time method resolution.
- The local variable
valis of typeint, and there is no exact match for anintparameter. - The compiler first checks for primitive widening, promoting the
intto along, which successfully matches the first method. - Boxing conversion (from
inttoInteger) is lower in the overload resolution hierarchy and is only considered if widening options are unavailable.
public class VarargsTest {
public static void show(int a, int b) {
System.out.println("Fixed args");
}
public static void show(int... a) {
System.out.println("Varargs");
}
public static void main(String[] args) {
show(1, 2);
}
}When multiple overloaded methods match a method invocation, the Java compiler prioritizes fixed-arity methods over variable-arity (varargs) methods.
- The method
show(int a, int b)is a fixed-arity method that exactly matches the two arguments provided. - The varargs method
show(int... a)can also accept two arguments, but it has lower precedence in overload resolution. - Consequently, the compiler selects the fixed-arity version.
null?public class NullOverload {
public static void test(Object obj) {
System.out.println("Object");
}
public static void test(String str) {
System.out.println("String");
}
public static void main(String[] args) {
test(null);
}
}When passing
null to overloaded methods with reference types, the Java compiler selects the most specific type.- Both
ObjectandStringcan acceptnullas a value. - Since
Stringis a subclass ofObject, it is considered more specific in the inheritance hierarchy. - The compiler resolves the call to the most specific applicable method, which is
test(String).
public class AmbiguityTest {
public static void run(String s) {
System.out.println("String");
}
public static void run(Integer i) {
System.out.println("Integer");
}
public static void main(String[] args) {
run(null);
}
}When passing
null to overloaded methods, the Java compiler looks for the most specific applicable type.- Both
StringandIntegercan acceptnullas a valid value. - However,
StringandIntegerare sibling classes in the object hierarchy (both inherit directly fromObject, but neither is a subclass of the other). - Because neither parameter type is more specific than the other, the compiler cannot resolve a unique match, resulting in a compile-time ambiguity error.
public class ResolutionPhaseTest {
public static void process(long val) {
System.out.println("Widening");
}
public static void process(int... val) {
System.out.println("Varargs");
}
public static void main(String[] args) {
int num = 42;
process(num);
}
}According to the Java Language Specification (JLS), overload resolution follows a strict multi-phase search order:
- Phase 1 checks for an exact type match.
- Phase 2 considers primitive widening conversions (such as
inttolong). - Phase 3 considers boxing and unboxing conversions.
- Phase 4 considers variable arity (varargs) methods.
Since primitive widening has a higher precedence than variable arity, the compiler selects process(long) during Phase 2.
public class RecordOverloadTest {
public record Point(int x, int y) {}
public static void process(Object obj) {
System.out.println("Object version");
}
public static void process(Record rec) {
System.out.println("Record version");
}
public static void main(String[] args) {
Point p = new Point(1, 2);
process(p);
}
}With modern Java specifications introducing Records, the
java.lang.Record class serves as the common base class for all record types.- A Java
recordimplicitly extendsjava.lang.Record, which in turn extendsObject. - When passing an instance of
Point, bothObjectandRecordare valid applicable types. - Because
Recordis a more specific type in the inheritance hierarchy thanObject, the compiler selects the overloaded method acceptingRecord.
Understanding the binding mechanism is a frequent conceptual interview question at major tech companies:
- Overloading (Static Polymorphism): The Java compiler determines which overloaded method to invoke based entirely on the declared (compile-time) types of the reference and arguments.
- Overriding (Dynamic Polymorphism): The Java Virtual Machine (JVM) determines which overridden method to execute at runtime based on the actual instance (object type) residing in memory, regardless of the reference type.
- Runtime values or contents of arguments do not impact overload resolution because the signature and types are locked in at compile time.
class Parent {
public void display(int x) {
System.out.println("Parent int: " + x);
}
}
class Child extends Parent {
public void display(double x) {
System.out.println("Child double: " + x);
}
}
public class Test {
public static void main(String[] args) {
Parent obj = new Child();
obj.display(10);
}
}Method overloading can occur across inheritance hierarchies, which is frequently tested in interviews to check understanding of static binding versus dynamic dispatch:
- The
Childclass inheritsdisplay(int x)fromParentand introduces an overloaded methoddisplay(double x). - Since the reference variable
objis of typeParent, the compiler restricts its search for matching methods to theParentclass definition during compile-time overload resolution. - The argument passed is
10(anint), which matchesParent‘sdisplay(int)exactly. - Even though the underlying object in memory is a
Childinstance, overloading resolution uses the reference type and static types, meaning the child’s overload is ignored for aParentreference.
public class HierarchyOverload {
public static void process(CharSequence cs) {
System.out.println("CharSequence version");
}
public static void process(String s) {
System.out.println("String version");
}
public static void main(String[] args) {
process("Hello Java");
}
}When multiple overloaded methods accept applicable types in an inheritance hierarchy, the Java compiler resolves the call by choosing the most specific type.
- The argument passed is a string literal
"Hello Java", which is of typeString. - Both
CharSequence(an interface implemented byString) andStringcan accept the argument. - Because
Stringis a subtype ofCharSequence,Stringis considered more specific thanCharSequence. - The compiler selects the most specific applicable method, which is
process(String).
public class DataProcessor {
public void process(int... values) {
System.out.println("Varargs version");
}
public void process(int[] values) {
System.out.println("Array version");
}
}In Java, variable arity (varargs) parameters are treated as arrays by the compiler behind the scenes.
- The method signature for
process(int... values)compiles down to taking anint[]array in bytecode. - Because
process(int[] values)is already explicitly defined in the same class, the compiler detects a duplicate method declaration due to identical type erasure. - To fix this compilation error, either remove one of the conflicting methods or ensure their signatures have distinct parameter types or structures.
import java.util.concurrent.Callable;
public class TaskResolver {
public static void submit(Runnable r) {
System.out.println("Runnable version");
}
public static void submit(Callable<String> c) {
System.out.println("Callable version");
}
public static void main(String[] args) {
submit(() -> "Hello");
}
}This is an expert-level scenario commonly encountered in modern Java concurrent programming:
- Both
Runnable(which has the abstract methodvoid run()) andCallable<V>(which hasV call() throws Exception) are functional interfaces with zero parameters. - The lambda expression
() -> "Hello"is compatible with both target types because a value-compatible lambda can be assigned to avoid-returning method (the return value"Hello"is simply ignored), and it also matches the return type ofCallable. - Because neither functional interface is more specific than the other in the compiler’s type-inference rules, the compiler cannot choose a single unambiguous method, resulting in a compile-time error.
public class GenericOverload {
public void process(java.util.List<String> list) {
System.out.println("String list");
}
public void process(java.util.List<Integer> list) {
System.out.println("Integer list");
}
}Generics in Java are implemented via type erasure:
- During compilation, generic type arguments (such as
<String>and<Integer>) are erased by the compiler. - Both
List<String>andList<Integer>erase to the same raw type:List. - As a result, the compiler perceives both methods as having identical signatures (
process(List)), leading to a duplicate method declaration compilation error.
public class AmbiguityMultiParam {
public static void compute(int x, long y) {
System.out.println("int-long");
}
public static void compute(long x, int y) {
System.out.println("long-int");
}
public static void main(String[] args) {
compute(5, 10);
}
}This is a classic expert-level interview question testing multi-parameter overload resolution rules:
- Both arguments passed in
compute(5, 10)areintliterals. - The first overload
compute(int, long)requires an exact match for the first parameter and a primitive widening conversion (inttolong) for the second parameter. - The second overload
compute(long, int)requires a primitive widening conversion for the first parameter and an exact match for the second parameter. - Because neither method is strictly more specific across all parameter positions, the compiler cannot choose one without arbitrary bias, leading to a compile-time ambiguity error.
public class ExceptionOverload {
public void process() throws java.io.IOException {
System.out.println("IOException version");
}
public void process() throws Exception {
System.out.println("Exception version");
}
}This is a common trick question used in professional-level Java interviews:
- In Java, a method’s signature is defined strictly by its name and its parameter list (number, type, and order of parameters).
- Return types and exception declarations (
throwsclauses) are not part of the method signature. - Because both methods have the name
processand an empty parameter list, the compiler views them as duplicate method declarations, resulting in a compile-time error.
public class PrecedenceTest {
public static void show(long a, long b) {
System.out.println("long-long");
}
public static void show(Integer a, Integer b) {
System.out.println("Integer-Integer");
}
public static void main(String[] args) {
int x = 5;
int y = 10;
show(x, y);
}
}According to the Java Language Specification (JLS), overload resolution prioritizes primitive conversion phases over boxing conversion phases:
- The arguments passed are both of primitive type
int. - The first overload
show(long, long)requires primitive widening conversions (frominttolong), which are evaluated in Phase 2 of method resolution. - The second overload
show(Integer, Integer)requires boxing conversions (frominttoInteger), which are evaluated in a later phase (Phase 3). - Because primitive widening takes precedence over boxing, the compiler selects
show(long, long).
Static methods participate fully in method overloading:
- Like instance methods, static methods can have multiple versions sharing the same name as long as their parameter lists are distinct.
- Because method overloading is resolved statically at compile time (static polymorphism), the presence of the
staticmodifier does not restrict overloading capabilities. - Overload resolution for static methods is determined entirely by the declared reference type and argument types during compilation.
public class WrapperOverload {
public static void process(Object obj) {
System.out.println("Object version");
}
public static void process(Long l) {
System.out.println("Long version");
}
public static void main(String[] args) {
int val = 10;
process(val);
}
}This is a classic trap question testing wrapper type conversion rules during overload resolution:
- The argument passed is a primitive
intwith value10. - Wrapper classes in Java (such as
Integer,Long,Double) do not widen into one another (anIntegercannot be implicitly converted to aLong). Therefore,process(Long)is not applicable because anintcannot box directly into aLong. - Instead, the
intboxes into its corresponding wrapper typeInteger, which then undergoes reference widening toObject(sinceIntegerimplementsSerializable`, `Comparable`, and extends `Object). - Thus, the compiler selects the
process(Object)method.
public class PrivateOverloadTest {
private void display(int a) {
System.out.println("Private int: " + a);
}
public void display(String s) {
System.out.println("Public String: " + s);
}
public static void main(String[] args) {
PrivateOverloadTest obj = new PrivateOverloadTest();
obj.display(100);
}
}Access modifiers do not affect method overloading rules in Java:
- Access modifiers (such as
private,protected, orpublic) and return types are not part of a method’s signature. - Overloading is determined strictly by having the same method name with a distinct parameter list (number, type, or order of parameters).
- Since
display(int)anddisplay(String)have different parameter types, they are valid overloads, regardless of their access levels. - Because the call is made from within the same class (`PrivateOverloadTest`), the
privatemethod is fully accessible and executes successfully.
final modifier?public class FinalOverloadTest {
public final void compute(int x) {
System.out.println("Final int version: " + x);
}
public void compute(double x) {
System.out.println("Non-final double version: " + x);
}
public static void main(String[] args) {
FinalOverloadTest obj = new FinalOverloadTest();
obj.compute(10);
}
}final methods cannot be overloaded.The
final modifier restricts method overriding, not method overloading:- A
finalmethod cannot be overridden by a subclass, but it can be freely overloaded within the same class or inherited and overloaded in a subclass. - Method signature uniqueness is determined by the method name and parameter types. The presence of the
finalkeyword does not form part of the signature. - When
obj.compute(10)is called, theintparameter matches the exact signature of thefinal compute(int)method, executing it successfully.
main method in a Java class?main method cannot be overloaded because it is a special entry point reserved exclusively for the JVM.main method can be overloaded just like any other method, but the JVM will only invoke the standard public static void main(String[] args) signature when executing the class from the command line.main method causes a compilation error due to reserved keyword conflicts.main will be automatically executed in sequence by the JVM before program startup.Understanding
main method overloading is a popular conceptual topic in Java interviews:- The word
mainis simply a standard method identifier, not a reserved keyword that restricts overloading. - You can define multiple overloaded versions of
mainwith different parameter lists (such as accepting anintor a custom object) within the same class. - However, the JVM is strictly designed to look for the exact entry point signature
public static void main(String[] args)when launching an application. Any other overloaded version will behave like a regular method and must be invoked explicitly in code.
public class ReturnTypeOverload {
public int compute(int x) {
return x * 2;
}
public double compute(int x) {
return x * 2.0;
}
}This error identification scenario highlights the fundamental rules of method signatures in Java:
- Method overloading requires distinct parameter signatures (differing in number, type, or order of parameters).
- Return types are not part of the method signature used by the compiler to differentiate methods.
- Even though one method returns an
intand the other returns adouble, their parameter lists are identical:(int x). - Consequently, the compiler detects a duplicate method declaration, resulting in a compile-time error.
public class AmbiguousVarargsError {
public static void display(int a, int... b) {
System.out.println("Int and varargs");
}
public static void display(int... a) {
System.out.println("Varargs only");
}
public static void main(String[] args) {
display(1);
}
}This is a sophisticated error identification question testing compiler behavior with overlapping varargs overloads:
- When calling
display(1), both overloaded methods are potentially applicable:display(int a, int... b)can match ifa = 1and the varargs arraybis empty (zero elements).display(int... a)can match if the varargs arrayacontains a single element[1].
- Because both methods are applicable under variable arity rules and neither is more specific than the other, the compiler cannot make an unambiguous choice, resulting in a compile-time ambiguity error.
public class ComplexAmbiguity {
public static void evaluate(long l, Integer i) {
System.out.println("Long-Integer");
}
public static void evaluate(Integer i, long l) {
System.out.println("Integer-Long");
}
public static void main(String[] args) {
evaluate(1, 2);
}
}This is an expert-level error identification scenario testing advanced JLS method resolution rules:
- Both arguments passed to
evaluate(1, 2)are primitiveintliterals. - The first overload
evaluate(long, Integer)requires primitive widening for the first argument and boxing conversion for the second argument. - The second overload
evaluate(Integer, long)requires boxing conversion for the first argument and primitive widening for the second argument. - Because both methods require a combination of conversion types that fall into the same resolution tier and neither method is strictly more specific across all parameters, the compiler cannot choose one unambiguously, resulting in a compile-time ambiguity error.
Analyzing the complexity and performance profile of method overloading reveals key architectural insights:
- Compile-Time Resolution: Method overloading is an example of static polymorphism (compile-time binding). The Java compiler performs the heavy lifting—evaluating argument types, precedence phases (exact match, primitive widening, boxing, varargs), and ambiguity checks—during compilation.
- Runtime Efficiency: Because the exact method target is determined and embedded directly into the bytecode (using instructions like
invokestaticorinvokevirtualwith a precise descriptor), the JVM executes the call with zero resolution or lookup overhead. - Contrast with Overriding: Unlike method overriding (which relies on runtime dynamic method dispatch via virtual method tables), overloading has no runtime search penalty.
router.route("PAYLOAD_DATA", true); is invoked?public class ServiceRouter {
public void route(String destination, boolean secure) {
System.out.println("Route with security flag");
}
public void route(Object payload, boolean... flags) {
System.out.println("Route with varargs flags");
}
public static void main(String[] args) {
ServiceRouter router = new ServiceRouter();
router.route("PAYLOAD_DATA", true);
}
}This scenario-based question demonstrates how overload resolution prioritizes fixed-arity methods over variable arity (varargs) methods:
- The first overload
route(String, boolean)is a fixed-arity method where both arguments (aStringliteral and a primitiveboolean) are exact matches. - The second overload
route(Object, boolean...)requires reference widening for the first argument (fromStringtoObject) and treats the second argument as a varargs array. - According to the Java Language Specification, the compiler evaluates fixed-arity methods before considering variable-arity methods. Because an exact-matching fixed-arity method is available, the compiler selects it immediately without ambiguity.
class Parent {
public void display(Object obj) {
System.out.println("Parent: Object version");
}
}
class Child extends Parent {
public void display(String str) {
System.out.println("Child: String version");
}
}
public class InheritanceOverloadTest {
public static void main(String[] args) {
Parent p = new Child();
p.display("Hello");
}
}This advanced object-oriented design question tests the interaction between compile-time overload resolution and runtime polymorphism:
- Compile-Time Overload Resolution: Method overloading is resolved entirely at compile time based on the declared (static) type of the reference variable, which is
Parent(not the runtime object typeChild). - During compilation, the compiler looks for a
display(String)method within theParent class. BecauseParentonly definesdisplay(Object), the compiler binds the call toParent.display(Object)using reference widening (matchingStringtoObject). - Overloading vs. Overriding: The
display(String)method inChildis an overload, not an override, because its parameter signature differs from the parent’s method. Since the reference type isParent, the compiler cannot see or select methods unique toChild. - At runtime, dynamic dispatch executes the bound method, resulting in “Parent: Object version”.
send methods to handle various data types. What is the output when client.send("Hello World"); is invoked against the following implementation?public class MessageClient {
public void send(CharSequence payload) {
System.out.println("CharSequence overload");
}
public void send(String payload) {
System.out.println("String overload");
}
public static void main(String[] args) {
MessageClient client = new MessageClient();
client.send("Hello World");
}
}This real-world API design scenario illustrates how the compiler resolves overlapping applicable methods using the “most specific type” rule:
- A string literal
"Hello World"is of typeString. Both overloaded methods are applicable becauseStringcan be assigned to bothStringand its superinterfaceCharSequence. - To resolve this, the Java compiler applies the most specific method rule. Since
String` is a subtype of `CharSequence, `String` represents a more specialized and specific type. - The compiler automatically selects the most specific applicable overload, routing the call to
send(String)safely without any ambiguity errors.




