Java

Java 상속(Inheritance) 이해하기 - 업캐스팅, 다운캐스팅

오은이 2025. 4. 14. 13:38

 

 

 

 

우선, 메서드와 생성자의 정확한 차이부터 짚고 가야한다.

 

 

 

메서드와 생성자 차이

구분 메서드 (Method) 생성자 (Constructor)
목적 객체가 가진 동작/기능을 정의 객체가 생성될 때 초기화
이름 아무 이름 가능 클래스 이름과 동일해야 함
반환 타입 있어야 함 (void, int, etc) ❌ 없음 (void도 안 씀)
호출 시점 객체가 만들어진 후 호출 new 클래스명() 할 때 자동 호출
호출 방법 객체.메서드() new 클래스()
상속/오버라이딩 메서드는 오버라이드 가능 생성자는 상속/오버라이드 불가 (다만 super()로 호출 가능)

 

 

 

 


 

 

 

업캐스팅과 다운캐스팅

 

개념 방향 설명
업캐스팅 자식 → 부모 자식 객체를 부모 타입으로 참조 (자동)
다운캐스팅 부모 → 자식 부모 타입 참조를 자식 타입으로 형변환 (명시적)
    단, 실제 객체가 자식 타입일 때만 가능

 

Parent p = new Child();   // 업캐스팅 (자동)
Child c = (Child)p;       // 다운캐스팅 (명시적으로)

 

 

 

 

 

예제1) 정보처리기사 실기 2023년 3회

public class main{
    public static void main(String[] args) {
        A b = new B();
        b.paint();
        b.draw();
    }
}
 
class A {
    public void paint() {
        System.out.print("A");
        draw();
    }
    public void draw() {
        System.out.print("B");
        draw();
    }
}
 
class B extends A {
    public void paint() {
        super.draw();
        System.out.print("C");
        this.draw();
    }
    public void draw() {
        System.out.print("D");
    }
}

 

출력 결과
BDCDD

 

코드 의미
this.draw() 오버라이딩된 메서드 호출됨 (B)
draw() 오버라이딩된 메서드 호출됨 (B)
super.draw() (B 내부) 부모의 draw 호출 가능 (A)
A 내부에서 강제로 A의 draw만 호출 불가능 → 다른 메서드로 분리해서 써야 함

 

 

 

 

 

 

 

 

 

예제2) 정보처리기사 실기 2024년 1회

class classOne {
    int a, b;
 
    public classOne(int a, int b) {
        this.a = a;
        this.b = b;
    }
 
    public void print() {
        System.out.println(a + b);
    }
 
}
class classTwo extends classOne {
    int po = 3;
    
    public classTwo(int i) {
        super(i, i+1);
    }
 
    public void print() {
        System.out.println(po*po);
    }
}
 
public class main {  
    public static void main(String[] args) {
        classOne one = new classTwo(10);
        one.print();
    }
}

 

출력 결과
9

 

코드 의미
super(i, i+1) 부모 클래스의 생성자 호출 (초기화)
super.print() 부모 클래스의 print() 메서드 호출
one.print() (오버라이딩된) 자식 클래스의 print() 호출

 

 

 

 

 

 

 

 

예제3) 정보처리기사 실기 2023년 1회

class Parent {
    int x = 100;
 
    Parent() {
        this(500);
    }
 
    Parent(int x) {
        this.x = x;
    }
 
    int getX() {
        return x;
    }
}
 
class Child extends Parent {
    int x = 4000;
    
    Child() {
        this(5000);
    }
 
    Child(int x) {
        this.x = x;
    }
}
 
public class Main {
    public static void main(String[] args) {
        Child obj = new Child();
        System.out.println(obj.getX());
    }
}

 

출력 결과
500

 

① new Child()로 child의 기본 생성자 호출

② 기본 생성자에서 this(5000) 호출하여 Child(int x)로 넘어감

③ Child(int x)는 super() 클래스가 숨겨진 형태

Child(int x) {
    super();      // 자동 삽입됨 ⬅ 부모 기본 생성자 호출
    this.x = x;   // Child.x = 5000
}

 

④ 그러므로 super(); → Parent() 생성자 호출

⑤ Parent(int x) 호출, Parent.x = 500

⑥ this.x → Parent의 x

⑦ 500 리턴

 

 

 

 

 

‼️생성자 호출용 this()가 없다면 super()가 자동 삽입됨

 

 

예제4) 정보처리기사 실기 2025년 1회

class Parent {
    static int total = 0;
    int v = 1;

    public Parent() {
        total += (++v);
        show();    
    }

    public void show() {
        total += total;
    }
}

class Child extends Parent {
    int v = 10;

    public Child() {
        v += 2;
        total += v++;
        show();
    }

    @Override
    public void show() {
        total += total * 2;
    }
}

public class Gisafirst {
    public static void main(String[] args) {
        new Child();
        System.out.println(Parent.total);
    }
}

 

출력결과
54

 

 

new Child(); 실행됨

Child() 생성자 진입

자동으로 super(); 호출

super(); → Parent() 생성자 실행 (v = 2, total = 2)

Parent() 내에서 show() 호출

⑥ 오버라이딩된 Child.show() 실행 (total = 6)

⑦ 그 후에 다시 Child 생성자 나머지 코드 실행 (v = 12, total = 18, v = 13)

⑧ show() 호출 → Child의 show 호출 (total = 18 + 18 * 2 = 54)

⑨ Parent.total 호출 54 출력