Why this matters
- Overload resolution follows strict rules, and when two candidates look equally good the compiler's choice is often not the one you expected.
- Constructor chaining is how you avoid duplicating validation across four constructors.
- An object that can be constructed in an invalid state will eventually be constructed in an invalid state.
input.add(pen) is visible to the caller. input = new Cart() is not — it only repoints the copy.
Method signatures
A signature is the method name plus its parameter types, in order. The return type is not part of it, and neither are parameter names.
int calculate(int a, int b) // signature: calculate(int, int)
double calculate(int a, int b) // same signature — will not compile
int calculate(int a, long b) // different signature — fine
Two methods differing only in return type cannot coexist, because the compiler would have no way to tell them apart at a call site.
Overloading
Overloading means several methods sharing a name but differing in parameters. The compiler chooses at compile time, in a fixed order of preference.
Resolution order
- Exact match on the declared types.
- Widening a primitive —
inttolongtofloattodouble. - Boxing a primitive into its wrapper.
- Varargs, considered last of all.
static void show(long value) { System.out.println("long"); }
static void show(Integer value) { System.out.println("Integer"); }
static void show(int... values) { System.out.println("varargs"); }
show(5); // prints "long"
Widening beats boxing, and boxing beats varargs. An int argument therefore widens to long
rather than boxing to Integer, which surprises most people the first time.
Varargs
Varargs let a method accept any number of arguments, which arrive as an array.
static int sum(int... numbers) {
int total = 0;
for (int number : numbers) total += number;
return total;
}
sum(); // 0 — an empty array, never null
sum(1, 2, 3); // 6
sum(new int[]{4, 5}); // 9 — an array is accepted directly
Rules
- Only one varargs parameter, and it must be last.
- Inside the method it behaves exactly like an array.
- An overload with a fixed parameter list always wins over the varargs version.
Constructors
A constructor has the class's name, no return type, and runs once per object. If you declare none, the compiler supplies a no-argument default — but the moment you declare any constructor, that default disappears.
public class Account {
private final String id;
private final String owner;
private double balance;
public Account(String id, String owner, double balance) {
if (id == null || id.isBlank()) {
throw new IllegalArgumentException("id is required");
}
if (balance < 0) {
throw new IllegalArgumentException("balance cannot be negative");
}
this.id = id;
this.owner = owner;
this.balance = balance;
}
public Account(String id, String owner) {
this(id, owner, 0.0); // chaining: must be the first statement
}
}
Chaining rules
this(...)calls another constructor in the same class.super(...)calls the parent's.- Either one must be the first statement, and you cannot use both in the same constructor.
- With no explicit call, the compiler inserts
super(). If the parent has no no-argument constructor, that is a compile error. - Put the real validation in the most complete constructor and have the others delegate to it, as above.
static versus instance
| Aspect | static member | instance member |
|---|---|---|
| Belongs to | The class | Each object |
| Copies in memory | Exactly one | One per object |
| Called via | ClassName.member() | reference.member() |
| Can use this | No — there is no instance | Yes |
| Can access the other kind | Only statics directly | Both |
| Overridable | No — it is hidden, not overridden | Yes |
Belongs to
static memberThe classinstance memberEach objectCopies in memory
static memberExactly oneinstance memberOne per objectCalled via
static memberClassName.member()instance memberreference.member()Can use this
static memberNo — there is no instanceinstance memberYesCan access the other kind
static memberOnly statics directlyinstance memberBothOverridable
static memberNo — it is hidden, not overriddeninstance memberYes
A static method cannot touch instance state, which is why main() needs an object to do anything.
Immutable parameters with final
Marking a parameter final prevents reassigning the parameter itself. It does not make the
object it points at immutable.
void process(final List<String> items) {
items.add("allowed"); // fine — mutating the object
// items = new ArrayList<>(); // compile error — reassigning the parameter
}
This is a useful habit in longer methods because it removes a whole class of confusion about which value a name currently holds.
Covariant return types
An override may narrow its return type to a subtype of the original. This matters for fluent
APIs and for clone().
class Shape {
Shape copy() { return new Shape(); }
}
class Circle extends Shape {
@Override
Circle copy() { return new Circle(); } // narrower return — legal
}
Callers holding a Circle now get a Circle back with no cast, while callers holding a Shape
still see a Shape. Widening the return type, by contrast, would break those callers and is
rejected.
Common misreadings
- "Overloading is resolved at run time." It is purely compile-time, based on declared types. Only overriding is resolved at run time.
- "A class always has a no-argument constructor." Declaring any constructor removes the compiler-generated default.
- "Return type can distinguish overloads." It cannot. Signatures are name plus parameter types only.
- "
staticmethods can be overridden." A subclass declaring the same static signature hides it. The call is resolved by the reference type, not the object. - "
finalparameters make the argument immutable." Only the binding is fixed; the object stays as mutable as it ever was.
Quick recall
Everything you need if you only revisit this box.
- A signature is name plus parameter types. Return type is excluded, so it cannot distinguish overloads.
- Overload resolution prefers exact match → widening → boxing → varargs, in that order.
nullarguments are ambiguous across unrelated reference overloads; cast to disambiguate.- Declaring any constructor removes the default no-argument one.
this(...)andsuper(...)must be the first statement, and only one of them may appear.- Validate in the fullest constructor and have the shorter ones delegate to it.
staticmembers are hidden, not overridden; the reference type decides which one runs.- Overrides may narrow the return type (covariant returns) but never widen it.
Test yourself
Answer these before moving on — recall is what makes it stick.