본문 바로가기
카테고리 없음

Java 객체지향 (상속, 오버라이딩, 오버로딩)

by ricepuppy9733 2026. 6. 19.

저도 처음엔 상속, 오버라이딩, 오버로딩이 그냥 비슷한 개념인 줄 알았습니다. 이름도 헷갈리고, 뭐가 다른지 제대로 구분이 안 됐습니다. 직접 챔피언 예제를 짜보면서 세 개념이 완전히 다른 목적으로 쓰인다는 걸 몸으로 익혔고, 그 과정에서 일반적으로 알려진 것과 실제 사용 방식이 꽤 다르다는 것도 알게 됐습니다.

상속, 오버라이딩, 오버로딩

상속과 오버라이딩: 코드 재사용이라는 말의 실제 의미

상속(Inheritance)은 기존 클래스의 변수와 메서드를 그대로 물려받아 새 클래스를 만드는 기능입니다. 여기서 상속이란 단순히 코드를 복사하는 게 아니라, 부모 클래스가 가진 구조 전체를 자식 클래스가 이어받아 활용하는 방식을 의미합니다. 일반적으로 "코드 재사용을 위해 상속을 쓴다"고 알려져 있는데, 저는 처음에 그 말이 그냥 복붙 아닌가 싶었습니다. 직접 써보니 전혀 달랐습니다.

챔피언 예제에서 Champion 클래스에는 이름, 좌표, HP, 레벨, 경험치, 공격력 같은 필드와 attack, hunting 같은 메서드가 있었습니다. JumpChampion은 거기에 z좌표 하나만 추가하면 되는 캐릭터였는데, 상속 없이 처음부터 다시 짜려고 했다면 Champion에 있는 필드와 메서드를 전부 다시 써야 했을 겁니다. extends 키워드 하나로 그 수고가 사라지니까 그때 "아, 이래서 재사용이구나"를 실감했습니다.

그런데 상속보다 더 헷갈렸던 게 오버라이딩(Overriding)이었습니다. 오버라이딩이란 부모에게 물려받은 메서드를 자식 클래스에서 같은 이름, 같은 매개변수로 다시 정의해 동작 방식을 바꾸는 것을 의미합니다. Champion의 attack 메서드는 "내가 누구를 공격했다"고 출력하지만, JumpChampion의 attack은 "점프해서 공격했다"고 출력합니다. 코드상으로는 메서드 이름도 같고 매개변수 타입도 같은데, 실행 결과가 달라집니다.

여기서 제가 실제로 헷갈렸던 부분이 있었습니다. 부모 생성자 문제입니다. Champion에 생성자를 만들고 나면 JumpChampion에서도 반드시 생성자를 따로 만들어야 하고, 그 안에서 super(name, region)으로 부모 생성자를 호출해줘야 합니다. 여기서 super란 부모 클래스의 생성자나 메서드에 접근할 때 쓰는 키워드로, 이걸 빠뜨리면 컴파일 에러가 납니다. 처음에 이걸 몰라서 에러를 잠깐 당황하며 봤는데, super 한 줄 넣으니 바로 해결됐습니다.

상속과 오버라이딩을 함께 쓸 때 핵심이 되는 개념 정리입니다.

  • extends 키워드로 부모 클래스를 지정하면 부모의 모든 필드와 메서드를 물려받는다
  • 오버라이딩은 부모에게서 받은 메서드를 자식 클래스에서 같은 시그니처(메서드 이름 + 매개변수)로 재정의하는 것
  • 자식 클래스에서 생성자를 만들 경우 super()로 부모 생성자를 반드시 호출해야 한다
  • 자바는 다중 상속을 허용하지 않으므로 extends 뒤에 클래스는 하나만 올 수 있다

객체지향 설계 원칙 중 하나인 SRP(단일 책임 원칙, Single Responsibility Principle)도 상속 구조를 잡을 때 영향을 줍니다. SRP란 하나의 클래스는 하나의 책임만 가져야 한다는 원칙으로, 이 때문에 Champion과 JumpChampion을 굳이 분리해서 만드는 겁니다. 모든 걸 Champion 하나에 때려 넣으면 나중에 유지보수가 힘들어지는 이유가 여기에 있습니다(출처: Oracle Java Documentation).

오버로딩: 같은 이름, 다른 매개변수의 실용적 의미

오버로딩(Overloading)은 처음 볼 때 오버라이딩과 이름이 비슷해서 더 헷갈렸습니다. 오버로딩이란 같은 클래스 안에서 메서드 이름은 동일하지만 매개변수의 개수나 타입이 다른 메서드를 여러 개 정의하는 것을 의미합니다. 상속과는 무관하고, 같은 클래스 내에서 동작합니다.

calc 예제에서 sum이라는 메서드를 숫자 2개짜리, 3개짜리, 4개짜리 버전으로 세 개 만든 것이 오버로딩의 전형입니다. 메서드 이름이 전부 sum인데, 매개변수 개수가 달라서 자바가 알아서 어떤 sum을 호출할지 판단합니다. 이걸 처음 봤을 때 "굳이 이름을 같게 해야 하나, 그냥 sumTwo, sumThree로 나누면 안 되나" 싶었는데, 실제로 쓰다 보니 호출하는 입장에서 훨씬 편합니다. sum 하나만 기억하면 되니까요.

챔피언 예제에서는 오버로딩이 더 직관적으로 나타납니다. Champion 클래스에 attack 메서드가 세 가지 버전으로 존재합니다. 하나는 특정 챔피언 하나를 공격하는 버전(Champion 타입 매개변수), 하나는 매개변수가 없어서 실수로 궁을 쓰는 상황을 표현한 버전, 또 하나는 Champion 배열을 받아서 여러 명을 동시에 공격하는 버전입니다. 이 세 개는 모두 attack이라는 이름을 공유하면서 매개변수 타입과 개수로 구분됩니다.

제가 직접 써봤는데 솔직히 오버로딩은 오버라이딩보다 훨씬 직관적으로 이해됐습니다. "같은 행동인데 상황이 다를 때" 쓴다고 생각하면 자연스럽게 이해됩니다.

오버로딩과 오버라이딩을 헷갈리지 않으려면 다음 기준으로 구분하면 됩니다.

  1. 오버로딩: 같은 클래스 안, 같은 이름, 다른 매개변수 → 여러 상황에 대응하는 메서드 묶음
  2. 오버라이딩: 부모-자식 관계, 같은 이름, 같은 매개변수 → 부모 메서드의 동작을 자식이 바꿈

캡슐화(Encapsulation)와도 연결되는 부분이 있습니다. 캡슐화란 필드를 private으로 숨기고 getter와 setter 메서드로만 외부 접근을 허용하는 설계 방식을 의미합니다. 오버라이딩을 제대로 활용하려면 부모 클래스의 필드가 private일 경우 자식에서 직접 접근이 안 되기 때문에, 챔피언 예제처럼 getName(), getHp() 같은 getter를 통해서 접근해야 합니다. 처음에 target.name으로 바로 접근하려다가 컴파일 에러를 맞은 게 기억납니다. 그때 캡슐화의 존재 이유를 실제로 느꼈습니다.

자바에서 객체지향의 핵심 개념들이 언어 차원에서 어떻게 구현되는지는 오라클 공식 문서에 잘 정리되어 있습니다(출처: Oracle Java Tutorials).

정리하면, 상속은 공통 구조를 재사용하기 위해, 오버라이딩은 그 구조를 자식 입장에 맞게 수정하기 위해, 오버로딩은 같은 맥락의 기능을 매개변수 조합에 따라 유연하게 대응하기 위해 씁니다. 세 개념 모두 처음엔 이름 때문에 뭉뚱그려 이해하기 쉬운데, 직접 예제를 짜면서 어떤 상황에 어떤 개념이 들어오는지 경험하는 게 가장 빠른 방법이었습니다. 먼저 Champion 클래스 하나를 완성한 다음, JumpChampion으로 확장하는 순서로 연습해보시면 세 개념이 한 번에 연결되는 걸 느낄 수 있을 겁니다.


참고: https://dltldnr2563.tistory.com/entry/%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%8020250710-Java%EB%AC%B8%EB%B2%95-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%83%81%EC%86%8D-%EC%BA%A1%EC%8A%90%ED%99%94-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 자동식단생성 연관 블로그