Java 상속(extends), super, this()와 필드 하이딩
요약
Java 상속(extends)의 개념과 super/this() 키워드, 생성자 체이닝, 필드 하이딩을 알아봅니다. 정보처리기사 실기에 출제된 상속 문제를 풀기 위한 핵심 개념을 정리합니다.
상속 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 상속 (extends) | 부모 클래스의 필드와 메서드를 물려받음 | class Square extends Rectangle |
| super() | 부모 클래스의 생성자를 호출 | super(s, s); |
| 생성자 체이닝 | this() → super() 순서로 생성자가 연결됨 | Rectangle() → Rectangle(int, int) → 부모 생성자 |
| 필드 하이딩 | 부모-자식 같은 이름 필드는 참조 타입 기준 | ref.x → 참조 변수 타입의 x |
상속이란? 쌩기초
상속(Inheritance)은 기존 클래스(부모)의 필드와 메서드를 새로운 클래스(자식)가 물려받는 것입니다.
쉽게 말하면, "부모가 가진 것을 자식이 그대로 사용할 수 있다" 는 뜻입니다.
위 코드에서 Square는 Rectangle을 상속받았기 때문에, 직접 선언하지 않아도 width, height, getArea()를 사용할 수 있습니다.

extends 키워드 쌩기초
자식 클래스가 부모 클래스를 상속할 때 extends 키워드를 사용합니다.
extends vs implements 비교
| 키워드 | 사용 대상 | 의미 | 예시 |
|---|---|---|---|
| extends | 클래스 -> 클래스 | 상속 (부모 기능을 물려받음) | class Square extends Rectangle |
| implements | 클래스 -> 인터페이스 | 구현 (약속한 기능을 만듦) | class A implements Person |
생성자, this, 기본 생성자에 대해서는 객체와 클래스 페이지를 참고하세요.
super 키워드 기초
super 는 부모 클래스를 가리키는 키워드입니다. super(...) 형태로 괄호와 함께 쓰면 부모 생성자를 호출하고, super.x 형태로 점(.)과 함께 쓰면 부모의 필드나 메서드에 접근합니다. 생성자에서 super()를 호출할 때는 반드시 첫 번째 줄에 위치해야 합니다.
super(s, s)는 부모 클래스인 Rectangle의 생성자 Rectangle(int width, int height)를 호출합니다. s가 width와 height 모두에 전달되므로, 정사각형(가로 = 세로)이 됩니다.
super를 반드시 명시해야 하는 경우
부모 클래스에 기본 생성자(매개변수 없는 생성자)가 없는 경우, 자식 클래스에서 반드시 super를 명시적으로 호출해야 합니다.
super를 생략할 수 있는 경우
부모 클래스에 기본 생성자가 있는 경우, 컴파일러가 자동으로 super()를 호출합니다.
super 정리 표
| 부모 클래스 상태 | super 필요 여부 | 이유 |
|---|---|---|
| 생성자 없음 (기본 생성자 자동 생성) | 생략 가능 | 컴파일러가 super() 자동 호출 |
| 기본 생성자 있음 | 생략 가능 | 컴파일러가 super() 자동 호출 |
| 매개변수 생성자만 있음 | 반드시 명시 | 기본 생성자가 없어서 자동 호출 불가 |
생성자 체이닝 심화
생성자 체이닝(Constructor Chaining)은 하나의 생성자가 다른 생성자를 호출하고, 그 생성자가 또 다른 생성자를 호출하는 구조입니다. 아래 예제에서는 Rectangle() → this(10, 20) → super("사각형") 순서로 연결됩니다.
실행 순서
new Rectangle()이 호출되면:
| 순서 | 코드 | 설명 |
|---|---|---|
| 1 | Rectangle() | 기본 생성자 진입 |
| 2 | this(10, 20) | Rectangle(int, int) 생성자 호출 |
| 3 | super("사각형") | Shape(String) 생성자 호출 |
| 4 | this.type = "사각형" | Shape의 type 필드에 저장 |
| 5 | this.width = 10 | Rectangle의 width 필드에 저장 |
| 6 | this.height = 20 | Rectangle의 height 필드에 저장 |
필드 하이딩 심화
부모 클래스와 자식 클래스에 같은 이름의 필드가 있으면, 자식의 필드가 부모의 필드를 가립니다. 이것을 필드 하이딩(Field Hiding)이라고 합니다. 부모의 필드가 사라지는 것이 아니라, 하나의 객체 안에 같은 이름의 필드가 두 개 존재하되, 자식의 필드가 부모의 필드를 가려서 보이지 않게 만드는 것입니다. 가려진 부모의 필드에 접근하려면 super.x를 사용합니다.
필드는 선언 타입 기준
Parent ref = new Child();에서 등호 왼쪽 Parent가 선언 타입이고, 등호 오른쪽 new Child()가 실제 객체의 타입입니다. 필드에 접근할 때는 선언 타입(= 등호 왼쪽) 기준으로 결정됩니다.
같은 Child 객체를 가리키더라도, 선언 타입이 Parent이면 Parent의 x가, 선언 타입이 Child이면 Child의 x가 출력됩니다. 이것은 Java의 규칙이므로 외워두세요.
필드 vs 메서드 호출 기준 비교
메서드 오버라이딩(자식이 부모의 메서드를 재정의하는 것)에서는 실제 객체 타입 기준으로 호출됩니다. 하지만 필드와 static 메서드는 선언 타입 기준입니다.
| 구분 | 호출 기준 | 예시 |
|---|---|---|
| 인스턴스 메서드 (오버라이딩) | 실제 객체 타입 (= 등호 오른쪽) | ref.method() → Child의 method() |
| 필드 (하이딩) | 선언 타입 (= 등호 왼쪽) | ref.x → Parent의 x |
| static 메서드 | 선언 타입 (= 등호 왼쪽) | ref.id() → Parent의 id() |
왜 이렇게 나뉠까? — 소속이 다르기 때문
이 규칙을 외우는 핵심은 "객체에 소속된 것만 실제 객체를 따른다" 입니다.
- 인스턴스 메서드: 객체에 소속 → 실행할 때 실제 객체를 확인 → 실제 객체 기준 (오버라이딩 적용)
- 필드: 클래스에 소속 → 컴파일할 때 선언 타입만 보고 결정 → 선언 타입 기준 (필드 하이딩)
- static 메서드: 클래스에 소속 → 컴파일할 때 선언 타입만 보고 결정 → 선언 타입 기준
이것을 정적 바인딩1과 동적 바인딩2이라고 부릅니다. "바인딩"은 호출 코드와 실제 실행될 코드를 연결하는 것이고, 언제 연결하느냐의 차이입니다.
| 구분 | 소속 | 바인딩 시점 | 기준 | 결과 |
|---|---|---|---|---|
| 인스턴스 메서드 | 객체 | 실행 시점 (동적) | 실제 객체 | 오버라이딩 적용 |
| 필드 | 클래스 | 컴파일 시점 (정적) | 선언 타입 | 필드 하이딩 |
| static 메서드 | 클래스 | 컴파일 시점 (정적) | 선언 타입 | 하이딩 |
클래스에 소속된 것(필드, static 메서드)은 컴파일할 때 타입만 보면 결정할 수 있고, 객체에 소속된 것(인스턴스 메서드)은 실행해봐야 실제 객체가 뭔지 알 수 있기 때문입니다.
실전 예제: Rectangle과 Square
이 예제는 25년 3회 기출문제와 동일한 구조입니다.
실행 순서 추적
| 순서 | 코드 | 설명 |
|---|---|---|
| 1 | new Square(10) | Square 생성자 호출 (s = 10) |
| 2 | super(10, 10) | Rectangle 생성자 호출 (width = 10, height = 10) |
| 3 | this.width = 10 | Rectangle의 width에 10 저장 |
| 4 | this.height = 10 | Rectangle의 height에 10 저장 |
| 5 | sq.getArea() | 부모의 getArea() 호출: 10 * 10 = 100 |
Square는 Rectangle을 상속받았기 때문에 getArea() 메서드를 직접 선언하지 않아도 사용할 수 있습니다.