In this tutorial, we have compiled a comprehensive collection of method overloading programs in Java for thorough practice.
Each program is explained in a clear, beginner-friendly way so you can easily understand how method overloading works in Java.
These hands-on examples are ideal for coding practice, college exams, viva voce, and technical interview preparation. Practicing them will help you master key concepts of method overloading.
If you are new to method overloading or want to refresh your knowledge, we recommend reading our detailed tutorial on method overloading in Java first. It covers the core theory from scratch, making these practice programs much easier to follow.
Java Method Overloading Program with Inheritance and Reference Types
Problem Statement: Write a Java program to demonstrate that method overloading is resolved at compile time based on the declared reference type (static type) of the argument rather than its actual runtime object type.
Concept Behind the Program
Method overloading is resolved at compile-time based on the static type (reference type) of the argument, not the runtime object type. When an overloaded method is invoked, the Java compiler looks strictly at the declared type of the reference variable passed into the call.
Program Code 1:
// Class hierarchy: C extends B, and B extends A
class A {
}
class B extends A {
}
class C extends B {
}
class Overloading {
void m1(A a) {
System.out.println("m1-A");
}
void m1(B b) {
System.out.println("m1-B");
}
void m1(C c) {
System.out.println("m1-C");
}
}
public class OverloadingTest {
public static void main(String[] args) {
Overloading obj = new Overloading();
// Scene 1: Reference type is A, object is A
A a = new A();
obj.m1(a);
// Scene 2: Reference type is B, object is B
B b = new B();
obj.m1(b);
// Scene 3: Reference type is C, object is C
C c = new C();
obj.m1(c);
// Scene 4: Reference type is B, object is C
B bc = new C();
obj.m1(bc);
// Scene 5: Reference type is A, object is B
A ab = new B();
obj.m1(ab);
}
}Output:
m1-A m1-B m1-C m1-B m1-A
In this example program:
- Scene 1 (
obj.m1(a)): The reference a is declared as type A. The compiler binds this call to m1(A a). - Scene 2 (
obj.m1(b)): The reference b is declared as type B. The compiler binds this call to m1(B b). - Scene 3 (
obj.m1(c)): The reference c is declared as type C. The compiler binds this call to m1(C c). - Scene 4 (
obj.m1(bc)): Although the runtime object isnew C(), the reference type isB (B bc = new C()). The compiler only evaluates the reference type B, binding the call to m1(B b). - Scene 5 (obj.m1(ab)): Even though the runtime object is
new B(), the reference variable ab is declared asA (A ab = new B()). The compiler resolves the call using reference type A, binding it to m1(A a).
Java Method Overloading Program with Multilevel Class Hierarchy
Problem Statement: Write a Java program demonstrating method overloading across a three-level user-defined class hierarchy, and show which method gets invoked when a literal null is passed.
Core Concept Behind the Program
When you pass a literal null as an argument to overloaded methods, the compiler inspects all applicable reference types. Since the literal null can fit into any object type, all methods qualify. In this case, the Java compiler chooses the lowest child class in the inheritance hierarchy.
For example, in inheritance hierarchy like:
- Parent → Child → GrandChild
If methods exist for all three:
- m1(Parent p)
- m1(Child c)
- m1(GrandChild gc)
Calling m1(null) calls m1(GrandChild) because GrandChild is at the very bottom of the chain.
Program Code 2:
// Class hierarchy: C extends B, and B extends A
class A {
}
class B extends A {
}
class C extends B {
}
public class Overloading {
void m1(A a) {
System.out.println("m1-A");
}
void m1(B b) {
System.out.println("m1-B");
}
void m1(C c) {
System.out.println("m1-C");
}
public static void main(String[] args) {
Overloading obj = new Overloading();
// Passing null argument
obj.m1(null);
}
}Output:
m1-C
In this example program:
- A literal null has no intrinsic type—it can represent a reference to any non-primitive type. Therefore, all three overloaded methods—m1(A), m1(B), and m1(C)—are applicable for the invocation obj.m1(null).
- When multiple methods are applicable for a call, the compiler chooses the most specific one.
- In this hierarchy:
- C is a subclass of B, and B is a subclass of A (C extends B extends A).
- Because C is the narrowest, most derived type among all applicable choices, m1(C c) is strictly more specific than m1(B b) and m1(A a).
Method Overloading Program with Object Hierarchy and null
Problem Statement: Write a Java program demonstrating method overloading where parameters include Object along with custom classes in an inheritance chain, and observe method selection when passing null.
Concept Behind the Program
java.lang.Object sits at the root of the Java class hierarchy. Every reference type implicitly or explicitly extends Object. When methods are overloaded with Object and its derived subtypes:
- m1(Object) is considered the least specific method.
- Subtypes are strictly more specific than Object.
When passing null, the compiler discards Object and chooses the deepest child class in the hierarchy.
Program Code 3:
// Class hierarchy: Object -> A -> B
class A {
}
class B extends A {
}
public class Overloading {
void m1(A a) {
System.out.println("m1-A");
}
void m1(B b) {
System.out.println("m1-B");
}
void m1(Object o) {
System.out.println("m1-C");
}
public static void main(String[] args) {
Overloading obj = new Overloading();
// Passing literal null
obj.m1(null);
}
}Output:
m1-B
In this example program, a literal null is compatible with any reference type. Therefore, all three methods qualify:
- m1(Object o)
- m1(A a)
- m1(B b)
Subtype Hierarchy:
- In Java, Object sits at the very top of every reference hierarchy:
- A is a subtype of Object.
- B is a subtype of A (and transitively a subtype of Object).
Because B is the most derived (narrowest) type in this chain, the compiler selects m1(B b), printing “m1-B”.
Method Overloading Program with Sibling Types and null Ambiguity
Problem Statement: Write a Java program with overloaded methods taking Object, a custom class A, and String, and explain why passing null results in a compile-time ambiguity error.
Program Code 4:
// Class A extends Object implicitly
class A {
}
public class Overloading {
void m1(A a) {
System.out.println("m1-A");
}
void m1(String s) {
System.out.println("m1-String");
}
void m1(Object o) {
System.out.println("I am in m1-Object");
}
public static void main(String[] args) {
Overloading obj = new Overloading();
// Compile-time error: Ambiguous method call
obj.m1(null);
}
}Output:
Unresolved compilation problem: The method m1(A) is ambiguous for the type Overloading
When multiple overloaded methods accept reference types that share a common superclass (java.lang.Object) but do not have an inheritance relationship with each other, passing null creates a compile-time ambiguity error. The compiler eliminates the superclass as less specific but cannot pick a method between two independent sibling types.
The Ambiguity Conflict:
The compiler is left with two methods: m1(A a) and m1(String s).
- A is not a subtype of String.
- String is not a subtype of A.
Both classes are at the exact same hierarchy level as direct children of Object (siblings). Since neither method is strictly more specific than the other, the Java compiler cannot choose between them and halts compilation with the error: reference to m1 is ambiguous.
Java Method Overloading Program: Exact Matches, Inheritance, and Autoboxing
Problem Statement: Write a Java program demonstrating method overloading with Object, String, and Integer parameter types, and observe how the compiler resolves calls using exact matching, autoboxing, and polymorphic upcasting.
Concept Behind the Program
In Java, method overloading is resolved at compile-time using static binding. When choosing the method to invoke, the compiler first looks for an exact type match. If an exact match is not directly found, it applies widening, followed by autoboxing, and finally polymorphic upcasting to the nearest accessible supertype.
Program Code 5:
public class XYZ {
void msg(Object obj) {
System.out.println("Good");
}
void msg(String str) {
System.out.println("Better");
}
void msg(Integer itr) {
System.out.println("Best");
}
public static void main(String[] args) {
XYZ obj = new XYZ();
// Call 1: Direct match for Object
obj.msg(new Object());
// Call 2: Direct match for String (String literal)
obj.msg("Scientech Easy");
// Call 3: Implicit upcasting to Object
obj.msg(new XYZ());
// Call 4: Direct match for String (String object)
obj.msg(new String());
// Call 5: Autoboxing int -> Integer
obj.msg(10);
// Call 6: Direct match for Integer
obj.msg(Integer.valueOf(0)); // Modern replacement for deprecated `new Integer(0)`
}
}Output:
Good Better Good Better Best Best
In this example program:
1. obj.msg(new Object()); → Prints “Good”
- The argument is explicitly of type Object. It finds an exact match with the method signature msg(Object obj).
2. obj.msg(“Scientech Easy”); → Prints “Better”
- “Scientech Easy” is a java.lang.String literal. Although String is a child class of Object, Java’s “most specific method” rule selects msg(String str) over msg(Object obj).
3. obj.msg(new XYZ()); → Prints “Good”
- The class XYZ does not inherit from String or Integer, but it implicitly extends java.lang.Object.
- Neither msg(String) nor msg(Integer) can accept an XYZ instance.
- The compiler upcasts new XYZ() to Object and matches msg(Object obj).
4. obj.msg(new String()); → Prints “Better”
- The argument is an explicit String instance. It finds an exact match with msg(String str).
5. obj.msg(10); → Prints “Best”
- The value 10 is a primitive int. Since there is no primitive msg(int) method available, Java automatically converts (autoboxes) the primitive int into an Integer wrapper object (int -> Integer).
- The compiler then matches msg(Integer itr).
6. obj.msg(new Integer(0)); → Prints “Best”
- The argument is explicitly of reference type Integer. It directly matches msg(Integer itr).
Ambiguity with Sibling Classes and null
Problem Statement: Write a Java program to demonstrate method overloading with Object, String, and Integer parameters, and observe why passing a literal null causes a compile-time ambiguity error, along with how to fix it.
Program Code 6:
public class XYZ {
void msg(Object obj) {
System.out.println("Good");
}
void msg(String str) {
System.out.println("Better");
}
void msg(Integer itr) {
System.out.println("Best");
}
public static void main(String[] args) {
XYZ obj = new XYZ();
// Causes compile-time error due to ambiguity
obj.msg(null);
}
}If you call obj.msg(null);, the program will fail to compile with an ambiguity error. The literal null has no specific type and is compatible with any reference type. Therefore, all three methods, such as msg(Object), msg(String), and msg(Integer) are qualified.
Both String and Integer are subclasses of Object (String extends Object, Integer extends Number extends Object). Since String and Integer are more specific than Object, msg(Object) is eliminated from consideration.
String is not a subclass of Integer, and Integer is not a subclass of String. Both are sibling classes under Object with no subtype relationship between them. They are unrelated sibling classes at the same level under Object, the compiler cannot determine which one is more specific.
Java Method Overloading Program with Primitive Widening
Problem Statement: Write a Java program to demonstrate method overloading where a primitive argument (byte, int) is passed, and observe how the compiler selects the appropriate overloaded method using primitive widening.
Core Concept Behind the Program
When primitive types are passed to overloaded methods, Java first looks for an exact match. If none is found, it applies widening primitive conversion (byte → short → int → long → float → double) and selects the narrowest matching primitive type before ever considering autoboxing.
Program Code 7:
public class PrimitiveWidening {
public static void m1(short a) {
System.out.println("short");
}
public static void m1(int a) {
System.out.println("int");
}
public static void m1(long a) {
System.out.println("long");
}
public static void main(String[] args) {
byte b = 5;
m1(b); // Widened to short (closest match)
m1(5); // Exact match for int literal
int i = 10;
m1(i); // Exact match for int variable
}
}Output:
short int int
In this example program:
- m1(b); calls m1(short) and prints “short”.
- The variable b is a byte.
- Java widens primitives in order: byte $\rightarrow$ short $\rightarrow$ int $\rightarrow$ long.
- Even though int and long can also accept a byte, Java always picks the closest type to avoid unnecessary conversion.
- Since short is the closest step up from byte, m1(short) is chosen.
- m1(5); calls m1(int) and prints “int”.
- In Java, any whole number written directly (like 5) is treated as a primitive int by default.
- Because an exact m1(int) method exists, Java calls it directly without any conversion.
- m1(i); calls m1(int) and prints “int”.
- The variable i is explicitly declared as an int.
- Java finds an exact match with m1(int) and invokes it immediately.
Method Overloading Program with Object Subtyping
Problem Statement: Write a Java program to demonstrate method overloading using Object and String parameter types, and show which method gets invoked when a String literal, an Object instance, and null are passed.
Core Concept Behind the Program
When you pass reference types (such as objects or literals) as an argument to the method call, the compiler checks which method parameter is the most specific subtype in the inheritance hierarchy. A subclass method is always preferred over a superclass method.
Program Code 8:
public class ObjectSubtyping {
public static void m1(Object a) {
System.out.println("Object");
}
public static void m1(String a) {
System.out.println("String");
}
public static void main(String[] args) {
m1("10"); // String literal passed
m1(new Object()); // Object instance passed
m1(null); // Literal null passed
}
}Output:
String Object String
In this example program:
- m1(“10”); calls m1(String) and prints “String”.
- “10” is a text literal of type String.
- Every String is also an Object, but Java always prefers the more specific child class over the generic parent class.
- Since String is more specific than Object, m1(String) is chosen.
- m1(new Object()); calls m1(Object) and prints “Object”.
- The object created is purely an instance of Object.
- A general Object cannot automatically fit into a String parameter. Therefore, m1(Object) is the only method that can accept it.
- m1(null); calls m1(String) and prints “String”.
- A literal null has no type, so it fits into both Object and String.
- To resolve the tie, Java picks the lowest class in the inheritance chain (the most specific type).
- Since String is a child of Object, m1(String) is selected without any ambiguity error.
Ambiguity with Sibling Classes Under Object
Problem Statement: Write a Java program to demonstrate how passing null to overloaded methods with sibling classes (e.g., String and Integer) leads to a compile-time ambiguity error.
Concept Behind the Program
When literal null is passed to overloaded methods whose parameters are reference types with no inheritance relationship between them (sibling classes under Object), the compiler cannot determine which one is more specific, causing a compile-time error.
Program Code 9:
public class OverloadAmbiguity {
public static void m1(Object a) {
System.out.println("Object");
}
public static void m1(String a) {
System.out.println("String");
}
public static void m1(Integer a) {
System.out.println("Integer");
}
public static void main(String[] args) {
m1("Hello");
m1(100);
m1(new Object());
// Ambiguous call (Fails to compile):
// m1(null);
}
}Output:
String Integer Object
In this example program:
- m1(“Hello”); calls m1(String) and prints “String” because String is more specific than Object.
- m1(100); calls m1(Integer) and prints “Integer”. Since 100 is a primitive int and there is no primitive m1(int) method, Java automatically converts (autoboxes) the int into an Integer object (int → Integer).
- m1(new Object()); calls m1(Object) and prints “Object” because the object passed is an explicit instance of Object.
- An Object cannot automatically fit into more specialized subclasses like String or Integer. Therefore, m1(Object) is the only method that can accept it, and it executes without any conflict.
- A literal null has no type, so it is a valid match for any object or class.
- When multiple methods can take null, Java always tries to pick the child class instead of the parent.
- If you have Object, String, and Integer, Java immediately drops Object because both String and Integer are more specific child classes.
- String does not inherit from Integer, and Integer does not inherit from String. They are unrelated “sibling” classes on the exact same level.
- Since the compiler cannot decide which sibling is more specific, it gives up and throws a compile-time ambiguity error.
How to fix it?
Cast ‘null’ explicitly to the intended type:
- m1((String) null); // Resolves to String
- m1((Integer) null); // Resolves to Integer
- m1((Object) null); // Resolves to Object
Java Method Overloading Program: Ambiguity with Primitive Widening
Problem Statement: Write a Java program to demonstrate method overloading ambiguity when multiple primitive parameters (int and long) can be widened symmetrically across different overloaded methods.
Concept Behind the Program
When an overloaded method takes multiple primitive arguments, the Java compiler attempts to resolve the call by widening parameters where needed. However, if multiple overloaded methods can satisfy the call by widening different arguments symmetrically, neither method is strictly more specific, resulting in a compile-time ambiguity error.
Program Code 10:
public class Overloaded {
public static void msg(long a, int b) {
System.out.println("Hello");
}
public static void msg(int a, long b) {
System.out.println("Hi");
}
public static void main(String[] args) {
msg(5L, 10); // First call: Compiles cleanly
msg(10, 11); // Second call: Compile-time error!
}
}Output:
Overloaded.java:13: error: reference to msg is ambiguous
msg(10, 11);
^
both method msg(long,int) in Overloaded and method msg(int,long) in Overloaded match
1 errorIn this example program:
- msg(5L, 10); calls msg(long, int) and prints “Hello”
- 5L is an explicit long literal, while 10 is an int literal.
The method msg(int, long) cannot accept 5L as its first parameter because Java does not allow automatic narrowing from long to int. - Therefore, msg(long, int) is the only applicable method, and it executes without any ambiguity.
- 5L is an explicit long literal, while 10 is an int literal.
- msg(10, 11); fails to compile due to ambiguity
- Both arguments (10 and 11) are primitive int literals.
- Java checks applicability through primitive widening (int → long):
- msg(long, int) — widens the 1st argument (int -> long), keeps the 2nd as int.
- msg(int, long) — keeps the 1st as int, widens the 2nd argument (int → long).
- Both methods are valid methods, so Java checks which one is more specific:
- Method 1 has a more specific 2nd parameter (int is more specific than long).
- Method 2 has a more specific 1st parameter (int is more specific than long).
- Since neither method is more specific across both parameters, the compiler throws a compile-time ambiguity error.
How to Fix the Ambiguity
To resolve the error on msg(10, 11), explicitly cast one of the arguments or pass an explicit literal suffix so Java knows which parameter to widen:
msg(10L, 11); // Calls msg(long, int) → Prints: Hello msg(10, 11L); // Calls msg(int, long) → Prints: Hi
Java Method Overloading Program: Ambiguity with Array Types
Problem Statement: Write a Java program with overloaded methods taking different single-dimensional primitive arrays (int[] and char[]), and observe the compiler error when passing null.
Concept Behind the Program
In Java, array types (like int[] and char[]) are full reference types that directly inherit from java.lang.Object. However, there is no inheritance relationship between different primitive array types.
When a literal null is passed to methods overloaded with unrelated array types, the compiler is unable to resolve the overloaded method to call, resulting in a compile-time ambiguity error.
Program Code 11:
public class Overloaded {
public static void test(int[] intArr) {
System.out.println("int array");
}
public static void test(char[] charArr) {
System.out.println("char array");
}
public static void main(String[] args) {
// Causes compile-time error due to ambiguity
test(null);
}
}Output:
Overloaded.java:12: error: reference to test is ambiguous
test(null);
^
both method test(int[]) in Overloaded and method test(char[]) in Overloaded match
1 errorIn this example program:
- The literal null is compatible with both array types.
- Arrays in Java are reference types, not primitives.
- A literal null is a valid argument for any reference type, making both test(int[]) and test(char[]) applicable methods.
- Both int[] and char[] extend java.lang.Object directly.
- The types are unrelated sibling arrays:
- An int[] is not a subtype of char[].
- A char[] is not a subtype of int[].
- The compiler cannot choose a “most specific” method and halts with an ambiguous reference error.
How to Fix the Ambiguity
To resolve the error, explicitly cast null to the specific array type you want to invoke:
test((int[]) null); // Explicitly calls test(int[]) -> Prints: int array test((char[]) null); // Explicitly calls test(char[]) -> Prints: char array
Java Method Overloading Program with Varargs Priority
Problem Statement: Write a Java program containing a fixed-parameter method with primitive widening and a varargs method with an exact data type, and demonstrate that primitive widening always takes higher precedence over varargs.
Concept Behind the Program
In Java, method resolution follows a strict 3-phase priority order:
- Phase 1: Subtyping and Primitive Widening (no autoboxing, unboxing, or varargs allowed).
- Phase 2: Autoboxing and Unboxing (no varargs allowed).
- Phase 3: Varargs (variable-arity invocation).
The compiler only proceeds to Varargs if no applicable method is found in Phase 1 or Phase 2. As a result, primitive widening always beats a varargs method, even if the varargs parameter matches the argument type exactly.
Program Code 12:
public class Overloaded {
public void test(int i) {
System.out.println("Int");
}
public void test(int... i) {
System.out.println("Int varargs");
}
public void test(char... c) {
System.out.println("Char varargs");
}
public static void main(String[] args) {
Overloaded obj = new Overloaded();
obj.test('a'); // First call
obj.test(10); // Second call
}
}Output:
Int Int
In this example program:
- obj.test(‘a’); calls test(int i) and prints “Int”.
- The argument ‘a’ is a primitive char.
- The compiler first checks Phase 1 (widening without varargs):
- Java allows primitive widening from char int. Therefore, test(int i) is a valid match during Phase 1.
- Even though test(char… c) seems like an “exact” type match for char, it requires varargs.
- Because Phase 1 takes precedence over Phase 3, the compiler chooses test(int i) immediately without ever checking the varargs methods.
- obj.test(10); calls test(int i) and prints “Int”.
- The argument 10 is an integer literal of type int. It matches the fixed-arity method test(int i) directly in Phase 1 as an exact match.
- The varargs method test(int… i) is ignored because fixed-arity methods always have higher priority than variable-arity methods.
Note: Methods with varargs (…) have the lowest priority in method overload resolution.
The Hierarchy at a Glance:
Priority Order: Exact Match > Widening > Autoboxing > Varargs (Lowest Priority)
Critical Follow-Up Scenario:
When would test(char… c) or test(int… i) be called?
Varargs methods are only invoked when no fixed-arity method can satisfy the call:
obj.test('a', 'b'); // Calls test(char... c) → Prints: Char varargs
obj.test(10, 20); // Calls test(int... i) → Prints: Int varargs
obj.test(); // Ambiguous! Both test(int...) and test(char...) match zero argumentsConclusion
In this tutorial, we covered the top 12 method overloading programs and their key rules. We looked at how Java handles primitive widening, autoboxing, and exact matches. We also saw why varargs methods always have the lowest priority.
In addition, we explored how Java resolves null. It always picks the most specific child class. However, passing null between unrelated sibling classes causes a compile-time ambiguity error. The same error happens when symmetric primitive widening creates a tie.
Practicing these scenarios will help you avoid subtle bugs and clear Java interview questions with ease. Try testing these code examples in your own IDE to build strong confidence!





