
자바 오버로딩과 오버라이딩 차이는 입문자도 자주 만나고, 면접에서도 자주 나오는 주제입니다. 그런데 정의만 외우면 실제 코드에서는 다시 섞이기 쉽습니다. 실전에서는 파라미터가 달라서 메서드를 고르는 문제인지, 상속된 동작을 자식이 바꾸는 문제인지를 먼저 보는 편이 훨씬 실용적입니다.
이번 글에서는 오버로딩과 오버라이딩의 한 줄 정의를 짧게 정리한 뒤, 메서드 시그니처, 호출 시점, @Override, static 메서드 주의점까지 실제 코드 판단 기준으로 쉽게 설명하겠습니다.
자바 오버로딩과 오버라이딩 차이
- 오버로딩은 같은 이름을 유지하고 파라미터 목록을 다르게 만드는 일이다
- 오버라이딩은 상속 관계에서 부모의 인스턴스 메서드를 자식이 같은 시그니처로 다시 정의하는 일이다
- 오버로딩은 어떤 메서드를 고를지의 문제이고, 오버라이딩은 어떤 구현이 실행될지의 문제에 가깝다
즉, 파라미터가 달라서 고르는 문제면 오버로딩, 상속된 동작을 바꾸는 문제면 오버라이딩이라고 기억하면 훨씬 오래 갑니다.


왜 헷갈릴까
둘 다 겉으로 보면 같은 이름의 메서드처럼 보이기 때문입니다. 하지만 자바가 보는 기준은 다릅니다. 오버로딩은 같은 클래스 안에서 파라미터 목록이 다른 메서드들을 구분하는 일이고, 오버라이딩은 상속 관계에서 부모의 동작을 자식 구현으로 교체하는 일입니다. 즉, 하나는 선택 규칙, 다른 하나는 동작 재정의입니다.
시그니처
이 주제에서 가장 먼저 잡아야 할 단어가 메서드 시그니처입니다. Oracle Java 튜토리얼은 시그니처를 메서드 이름과 파라미터 타입으로 설명합니다. 이 말은 return type이 시그니처 구분 기준이 아니라는 뜻이기도 합니다.
int parse(String value) {
return Integer.parseInt(value);
}
long parse(String value) {
return Long.parseLong(value);
}위 코드는 이름도 같고 파라미터도 같기 때문에 허용되지 않습니다. 오버로딩은 return type이 아니라 파라미터 목록으로 구분합니다.
오버로딩
오버로딩은 같은 행동을 비슷한 의미로 제공하되, 입력 형태만 조금 다르게 받고 싶을 때 자주 씁니다. 즉, 이름을 바꾸지 않고도 같은 행동 축을 유지할 수 있게 해주는 방식입니다.
class NotificationSender {
void send(String message) {
System.out.println("text :: " + message);
}
void send(String message, boolean urgent) {
System.out.println("urgent=" + urgent + " :: " + message);
}
void send(String title, String message) {
System.out.println(title + " :: " + message);
}
}세 메서드는 모두 send이지만 파라미터 목록이 다르므로 서로 다른 메서드입니다. 이런 구조의 핵심은 같은 종류의 행동을 여러 입력 형태로 받는다는 점입니다. 다만 공식 튜토리얼도 말하듯, 오버로딩을 남발하면 오히려 API가 읽기 어려워질 수 있습니다.
오버라이딩
오버라이딩은 부모가 가진 인스턴스 메서드의 약속을 자식이 자기 방식으로 다시 구현하는 일입니다. 그래서 오버라이딩은 같은 이름의 새 메서드를 추가하는 일이 아니라, 부모 동작을 자식 상황에 맞게 바꾸는 일에 더 가깝습니다.
class Animal {
void speak() {
System.out.println("some sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("bark");
}
}여기서 Dog는 Animal의 speak()를 같은 시그니처로 다시 정의합니다. 이 감각이 바로 다형성과 연결됩니다.
호출 시점
실전에서 둘을 가장 잘 구분하는 질문은 이것입니다. 자바가 이 메서드를 언제 고르느냐? 오버로딩은 전달한 인자 형태를 기준으로 어떤 메서드를 선택할지 구분하고, 오버라이딩은 실제 객체 타입에 따라 어느 구현이 실행될지 달라집니다.
오버로딩 호출
class Printer {
void print(int value) {
System.out.println("int :: " + value);
}
void print(String value) {
System.out.println("String :: " + value);
}
}
Printer printer = new Printer();
printer.print(10);
printer.print("hello");여기서는 인자 타입이 다르므로 자바가 다른 print()를 고릅니다. 이게 오버로딩입니다.
오버라이딩 호출
class Animal {
void speak() {
System.out.println("animal");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("dog");
}
}
Animal animal = new Dog();
animal.speak();여기서는 변수 타입이 Animal이어도 실제 객체가 Dog이므로 Dog.speak()가 실행됩니다. 즉, 오버로딩은 메서드 선택의 문제이고, 오버라이딩은 다형성에 따른 실행 구현의 문제라고 보면 됩니다.
@Override
오버라이딩을 할 때는 @Override를 붙이는 습관이 좋습니다. 이건 장식이 아니라 실수 방지 장치에 가깝습니다. 실제로 override되는 메서드가 없으면 컴파일러가 오류로 잡아주기 때문입니다.
class Parent {
void work() {
System.out.println("parent");
}
}
class Child extends Parent {
@Override
void work() {
System.out.println("child");
}
}반대로 파라미터를 바꿔서 아래처럼 메서드를 만들면 override가 아니라 새로운 메서드를 추가한 것입니다. 즉, 부모 동작을 바꾼 것이 아니라 사실상 overloading에 가까운 상황입니다.
class Parent {
void work() {
System.out.println("parent");
}
}
class Child extends Parent {
void work(String target) {
System.out.println("child :: " + target);
}
}static
이 부분은 짧게라도 정확히 짚는 편이 좋습니다. static 메서드는 오버라이딩 대상이 아닙니다. Java 공식 튜토리얼은 이것을 hiding이라고 설명합니다. 그래서 인스턴스 메서드 오버라이딩과 같은 감각으로 보면 안 됩니다.
class Animal {
static void info() {
System.out.println("animal");
}
}
class Dog extends Animal {
static void info() {
System.out.println("dog");
}
}입문 단계에서는 이 문장만 기억해도 충분합니다. 인스턴스 메서드는 오버라이딩될 수 있지만, static 메서드는 오버라이딩이 아니라 hiding입니다.
실무 판단
- 같은 행동을 여러 입력 형태로 받고 싶다면 오버로딩을 먼저 떠올린다
- 부모 타입의 공통 약속은 유지하면서 구현을 바꾸고 싶다면 오버라이딩을 먼저 떠올린다
- 이름만 같고 실제 책임이 다르면 오버로딩을 억지로 쓰지 않는다
- 부모 메서드를 바꾸려는 의도인데 파라미터가 달라졌다면 override가 아니라 새 메서드일 가능성을 본다
짧게 줄이면 이렇습니다. 오버로딩은 입력 형태를 넓히는 것, 오버라이딩은 상속된 동작을 바꾸는 것입니다.
자주 하는 실수
- return type만 다르면 오버로딩이라고 생각하는 실수
- 이름만 같으면 오버라이딩이라고 생각하는 실수
- 오버라이딩하려다 파라미터를 바꿔서 새 메서드를 만든 실수
- static 메서드도 오버라이딩이라고 부르는 실수
특히 세 번째 실수는 생각보다 흔합니다. 부모 동작을 바꾸려는 의도라면 @Override를 붙여 먼저 검증하는 습관이 꽤 큰 도움이 됩니다.
기억할 기준
- 파라미터가 다르다 -> 오버로딩을 먼저 의심
- 부모 메서드를 자식이 다시 구현한다 -> 오버라이딩을 먼저 의심
- 어떤 메서드 이름 변형을 고를지 문제다 -> 오버로딩 감각
- 실제로 어떤 구현이 실행될지 문제다 -> 오버라이딩 감각
이 기준만 잡혀도 자바 메서드 관련 헷갈림이 꽤 많이 줄어듭니다. 정의를 외우는 것보다 질문 순서를 몸에 붙이는 편이 훨씬 오래 갑니다.
마무리
자바 오버로딩과 오버라이딩 차이는 면접용 한 줄 정의로만 외우면 금방 다시 섞입니다. 반대로 메서드 시그니처, 호출 시점, 상속 관계, 실제 객체 타입이라는 기준으로 보면 꽤 단순해집니다. 결국 오버로딩은 메서드 선택 규칙이고, 오버라이딩은 다형성에 따른 동작 교체라고 이해하면 훨씬 오래 갑니다.
관련해서 자바 비교 문법을 함께 보고 싶다면 자바 ==와 equals 차이, 추상 클래스와 인터페이스 차이 글도 이어서 읽어보면 좋습니다. 공식 기준은 Oracle Defining Methods, Overriding and Hiding Methods, JLS Chapter 8를 참고하면 됩니다.