부모 생성자 호출 + 오버라이딩 짬뽕

상곤·2025년 4월 18일

Java

목록 보기
19/22
post-thumbnail

Class

class A {
    int cost = 1;

    A() {
        System.out.println("A");
        outputCost();
    }

    void outputCost() {
        System.out.println(cost);
    }
}

class B extends A {
    int cost = 2;

    B() {
        System.out.println("B");
        outputCost();
    }

    @Override
    void outputCost() {
        System.out.println(cost);
    }
}

Main

public class Main {
    public static void main(String[] args) {
        A b = new B();
        b.outputCost();
    }
}

이때 출력이 어떻게 될까?

B
2
2

라고 생각한 사람은, 이 글(부모 클래스 생성자는 자동으로 실행된다?)을 읽고 오기를 추천한다.

A
1 ←
B
2
2

라고 생각했거나,

A
2 ←
B
2
2

라고 생각했다면

그래도 이 글(같은 이름의 메서드지만 다르게 실행되는 이유)까지는 알고 있는 사람일 것이다.

근데 정답은

A
0
B
2
2

이다..⁉

왜 0이 나올까??

위에서 내가 말한 두 글을 모두 이해한 사람이라면
어떻게 생각했을지에 맞춰서 코드 흐름을 한 번 따라가보자.

객체 생성

public class Main {
    public static void main(String[] args) {
        A b = new B(); ←
        b.outputCost();
    }
}

new B Class에 의해서 B Class 생성자가 호출된다.

B Class 생성자

    B() {
        System.out.println("B");
        outputCost();
    }

B Class는 A Class를 상속 받은 자식 Class이기 때문에, 부모 Class의 생성자가 먼저 호출된다.

즉, 코드로 엄밀히 따져 보자면

    B() {
    	super() ← 부모 클래스 생성자 호출
        System.out.println("B");
        outputCost();
    }

이렇게 부모 클래스 생성자를 먼저 호출한다.

A Class 생성자

    A() {
        System.out.println("A");outputCost();
    }

해당 프린트문이 실행되면서 출력으로는 A가 찍힌다.

그런다음 우리가 헷갈린 부분이다.

A.outputCost()

outputCost()은 어느것이 실행될까?

이 부분은 동적 바인딩의 개념이 필요하다.

동적 바인딩이랑 실행되는 시점에 JVM이 객체의 실체 타입을 보고 어떤 메서드를 실행할지 결정한다는 것이다.

현재 객체는 자식 객체인 B 클래스로 생성되었고,
현재 B 클래스 내부에는 outputCost() 메서드가 오버라이딩 되어 있다.

그렇기에 A 클래스의 메서드가 아닌,
B 클래스의 메서드가 실행되는 것이다.

B.outputCost() 🏳‍🌈

    @Override
    void outputCost() {
        System.out.println(cost);
    }

이 코드가 실행된다.

근데 이때 cost 값은 몇일까?

0이다!

왜냐면 부모 생성자부터 호출했기 때문에
자식 클래스인 B는 아직 생성이 되지 않았기 때문이다.

생성자가 실행이 되어야 비로소 인스턴스 영역이 힙 메모리에 탑재가 된다.

B 클래스의 '클래스 정보'(메서드, 변수 선언 등)는 이미 메소드 영역에 올라가 있지만,
실제로 인스턴스 변수(cost = 2)가 설정되는 'B 생성자'는 아직 실행되지 않았기 때문에
기본값 0으로 남아있다.

그렇기에 0이 출력된다.

B Class 생성자

    B() {
    	super(); 
        System.out.println("B");outputCost();}

B가 출력으로 나온다.

outputCost(); 부분은 오버라이딩된 B 클래스의 메서드가 실행되므로 초기화된 인스턴스 변수 2가 출력으로 나온다.

b.outputCost()

B의 인스턴스 변수가 2로 초기화 돼 있기에 2가 출력으로 나온다.


이 문제를 통해 자바에서 객체 생성 순서와 동적 바인딩,
그리고 초기화 타이밍의 미묘한 차이를 명확히 이해할 수 있었다.
앞으로 생성자 내부에서 오버라이딩된 메서드를 호출할 때는 이런 타이밍을 꼭 고려해야겠다...🤮

profile
🫠

0개의 댓글