자바 객체 지향 프로그래밍(OOP)의 핵심 개념인 클래스 상속과 메소드 오버라이딩에 대해 알아보겠습니다.
여기에 더해, 상속 관계에서 private, protected, static 변수가 어떻게 동작하는지 실제 예제 코드를 통해 명확하게 파악해 보겠습니다.
예제 코드 및 실행 결과
먼저 전체 예제 코드와 그 실행 결과를 살펴보겠습니다. Animal이라는 부모 클래스와 이를 상속받는 Dog 자식 클래스를 통해 개념을 이해해 봅시다.
// Animal.java - 부모 클래스
class Animal {
// private 변수: 해당 클래스 내부에서만 접근 가능
private String name;
// protected 변수: 상속받은 자식 클래스에서 접근 가능
protected int soundCount = 0;
// static 변수: 클래스에 속하며 모든 객체가 공유
private static int animalCount = 0;
// 생성자
public Animal(String name) {
this.name = name;
animalCount++; // 객체가 생성될 때마다 static 변수 1 증가
System.out.println("-> Animal 생성자 호출: '" + this.name + "' 생성. (총 동물 수: " + animalCount + ")");
}
// Getter
public String getName() {
return name;
}
public void makeSound() {
this.soundCount++; // 메소드 호출 시 인스턴스 변수 1 증가
System.out.println("[소리 횟수: " + this.soundCount + "] " + this.getName() + "이(가) 소리를 냅니다.");
}
// static 메소드: 클래스 이름으로 직접 호출 가능
public static int getAnimalCount() {
return animalCount;
}
}
// Dog.java - 자식 클래스
class Dog extends Animal {
// 생성자
public Dog(String name) {
// 부모 클래스의 생성자 호출 (animalCount도 여기서 증가됨)
super(name);
System.out.println("-> Dog 생성자 호출");
}
// 메소드 오버라이딩
@Override
public void makeSound() {
this.soundCount++; // 부모의 protected 변수에 접근하여 1 증가
System.out.println("[소리 횟수: " + this.soundCount + "] " + this.getName() + "이(가) 멍멍 짖습니다! 🐕");
}
public void wagTail() {
System.out.println(this.getName() + "이(가) 꼬리를 흔듭니다.");
}
}
// Main.java
public class Main {
public static void main(String[] args) {
System.out.println("--- 첫 번째 Dog 객체 생성 ---");
Dog myDog1 = new Dog("뽀삐");
myDog1.makeSound(); // 뽀삐의 soundCount가 1로 증가
myDog1.makeSound(); // 뽀삐의 soundCount가 2로 증가
System.out.println("\n--- 두 번째 Dog 객체 생성 ---");
Dog myDog2 = new Dog("해피");
myDog2.makeSound(); // 해피의 soundCount가 1로 증가
System.out.println("\n--- 뽀삐가 다시 소리를 냅니다 ---");
myDog1.makeSound(); // 뽀삐의 soundCount가 3으로 증가 (해피의 count와 무관)
System.out.println("\n--------------------");
// static 변수는 클래스 이름을 통해 직접 접근
System.out.println("지금까지 생성된 총 동물의 수: " + Animal.getAnimalCount());
}
}
실행결과
--- 첫 번째 Dog 객체 생성 ---
-> Animal 생성자 호출: '뽀삐' 생성. (총 동물 수: 1)
-> Dog 생성자 호출
[소리 횟수: 1] 뽀삐이(가) 멍멍 짖습니다! 🐕
[소리 횟수: 2] 뽀삐이(가) 멍멍 짖습니다! 🐕
--- 두 번째 Dog 객체 생성 ---
-> Animal 생성자 호출: '해피' 생성. (총 동물 수: 2)
-> Dog 생성자 호출
[소리 횟수: 1] 해피이(가) 멍멍 짖습니다! 🐕
--- 뽀삐가 다시 소리를 냅니다 ---
[소리 횟수: 3] 뽀삐이(가) 멍멍 짖습니다! 🐕
--------------------
지금까지 생성된 총 동물의 수: 2
핵심 개념 정리
코드와 결과를 바탕으로 자바의 주요 특징들을 하나씩 살펴보겠습니다.
1. 클래스 상속과 메소드 오버라이딩
- 상속 (extends): Dog 클래스는 extends Animal을 통해 Animal 클래스의 모든 public, protected 멤버(변수, 메소드)를 물려받습니다. 이를 통해 코드 재사용성을 높이고 논리적인 계층 구조를 만듭니다.
- 메소드 오버라이딩 (@Override): 자식 클래스(Dog)가 부모 클래스로부터 물려받은 메소드(makeSound)를 자신에게 맞게 재정의하는 것입니다. 예제에서 Dog 객체는 "멍멍 짖습니다!"라고 소리를 내도록 makeSound 메소드의 동작을 변경했습니다.
2. 변수의 종류와 접근 제어자
이 예제는 변수의 성격을 이해하는 데 아주 좋습니다.
- private String name: private 변수는 오직 선언된 클래스(Animal) 내부에서만 접근할 수 있습니다. 자식 클래스(Dog)조차 직접 접근할 수 없죠. 따라서 값을 가져오려면 public으로 공개된 getName() 같은 Getter 메소드를 이용해야 합니다. 이는 객체의 데이터를 보호하는 캡슐화의 핵심입니다.
- protected int soundCount: protected 변수는 선언된 클래스 내부와, 이 클래스를 상속받은 자식 클래스까지 접근을 허용합니다. Dog 클래스의 makeSound 메소드가 this.soundCount에 직접 접근하여 값을 증가시킬 수 있었던 이유입니다. 이 변수는 객체마다 따로 생성되는 인스턴스 변수이므로, '뽀삐'의 soundCount와 '해피'의 soundCount는 서로 영향을 주지 않습니다.
- private static int animalCount: static 변수는 객체가 아닌 클래스에 소속된 변수입니다. 따라서 모든 Animal과 Dog 객체들이 단 하나의 animalCount 변수를 공유합니다. new Dog(...)를 통해 객체가 생성될 때마다 생성자가 호출되어 공용 변수인 animalCount가 1씩 증가하는 것을 볼 수 있습니다.
코드 실행 흐름 분석
- new Dog("뽀삐"): Dog 생성자가 호출되고, 내부의 super("뽀삐")가 Animal 부모 생성자를 호출합니다. animalCount가 1이 됩니다.
- myDog1.makeSound(): '뽀삐' 객체의 makeSound가 호출되어 soundCount가 1, 2로 증가합니다.
- new Dog("해피"): 다시 Animal 생성자가 호출되어 공유 변수인 animalCount가 2가 됩니다. '해피' 객체는 자신만의 soundCount를 가집니다.
- myDog2.makeSound(): '해피' 객체의 soundCount가 1이 됩니다. ('뽀삐'의 soundCount와는 무관)
- myDog1.makeSound(): '뽀삐' 객체의 soundCount가 3으로 증가합니다.
- Animal.getAnimalCount(): 마지막으로 static 메소드를 통해 모든 객체가 공유하는 animalCount의 최종 값인 2를 출력합니다.
'Programming' 카테고리의 다른 글
| [Python 코드 분석] 트리 구조 생성과 홀수 레벨 노드의 합계 구하기 (0) | 2025.07.11 |
|---|---|
| [Java] 재귀 함수(Recursive Function)로 피보나치 수열 예제로 쉽게 이해하기 (0) | 2025.07.11 |
| 함수 호출 방식에 대한 이해: Python, C, C++, Java 비교 (1) | 2024.09.29 |
| 프로그래밍 언어 개념 Chapter 2.1 :: 언어의 변천 (0) | 2023.09.27 |
| 프로그래밍 언어 개념 Chapter 1.1 :: 프로그래밍 언어 소개 (0) | 2023.09.11 |