Java

[Java] 객체 지향 프로그래밍 - 상속 (Inheritance)

공대생안씨 2024. 7. 8. 22:58

1. 상속

  • 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해 줌
  • “기존 클래스의 속성과 기능을 물려받는 것”
  • extends 키워드 사용
  • extends : -> (”내가 상대방을 안다”) 라고 생각 가능
public class ElectricCar extends Car

// public class ElectricCar -> Car 라고 생각 가능

 

📌 상속관계 용어

부모 클래스 (슈퍼 클래스)
    - 상속을 통해 자신의 필드와 메서드를 물려주는 클래스
    - 자식 클래스에 대해 전혀 모름

자식 클래스 (서브 클래스)
    - 부모 클래스에게서 필드와 메서드를 상속받는 클래스

 

  • 상속 예시 구조도

  • 상속 예시 코드
public class Car {
	public void move() {
		System.out.println("차를 움직입니다.");
	}
}

public class ElectricCar extends Car {
	public void charge() {
		System.out.println("차를 충전합니다.");
	}
}

public class GasCar extends Car {
	public void fillup() {
		System.out.println("기름을 주유합니다.");
	}
}

 

📌 다중 상속 불가! / 단일 상속만 가능!

 

1-1. 상속과 메모리 구조

// 상속받은 ElectricCar 클래스의 인스턴스 생성
ElectricCar electricCar = new ElectricCar();

  • new ElectircCar()
    • ElectricCar 클래스의 인스턴스 생성
    • 또한 부모 클래스인 Car 인스턴스도 생성!!
  • 참조값은 1개지만 내부에는 2개의 클래스 정보 (자기 자신, 부모 클래스) 정보가 존재함!
📌 상속 관계

단순히 부모 클래스의 멤버 변수, 메서드 를 물려받는 것이 아님

인스턴스 생성 시 부모 클래스도 함께 생성되는 것임!!

 

1-1-1. 상속과 메모리 구조 - 메서드 호출

// 상속받은 ElectricCar 객체의 메서드 호출
electricCar.charge();

// 부모 클래스의 메서드 호출
electricCar.move();

"상속 관계의 경우 ⇒ 인스턴스 내부에는 부모와 자식이 모두 존재, 어디서 메서드 찾을것인가?"

  • 메서드 호출 과정
    1. 먼저 호출하는 변수의 타입(클래스)을 기준으로 선택
    2. 해당 기능이 없으면 부모 타입으로 올라가서 찾음 (반복)
      • 최상위 부모에도 메서드가 존재하지 않으면 컴파일 에러!
  • electricCar.charge();

  • electricCar.move();

 

1-2. 메서드 오버라이딩

  • 자세한 내용은 아래 링크

https://blogan99.tistory.com/62

 

[Java] 메서드 오버로딩, 메서드 오버라이딩 (Method Overloading, Method Overriding)

1. 메서드 오버로딩 (Method Overloading)자바에서 같은 이름의 메서드를 여러개 정의하는 것을 오버로딩 (Overloading) 이라고 함즉, 이름이 같고 매개변수가 다른 메서드를 여러 개 정의하는 것을 의미 

blogan99.tistory.com

 

1-3. 상속과 접근 제어

1-3-0. 접근 제어자

https://blogan99.tistory.com/114

 

[Java] 접근 제어자 (Access Modifier)

1. 접근 제어자해당 클래스 외부에서 특정 필드나 메서드에 접근하는 것을 허용 or 제한좋은 프로그램이란?: 무한한 자유도가 주어지는 프로그램이 아니다.⇒ 적절한 제약을 제공하는 프로그램

blogan99.tistory.com

 

1-3-1. 상속에서의 접근 제어자 사용 예시

public class Parent {

	public int publicValue;  // public 멤버 변수
	protected int protectedValue;  // protected 멤버 변수
	int defaultValue;  // default 멤버 변수
	private int privateValue;  // private 멤버 변수
	
	// public 메서드
	public void publicMethod() { System.out.println("Parent.publicMethod); }

	// protected 메서드
	protected void protectedMethod() { System.out.println("Parent.protectedMethod); }
	
	// default 메서드
	void defaultMethod() { System.out.println("Parent.defaultMethod); }

	// private 메서드
	private void privateMethod() { System.out.println("Parent.privateMethod); }
}

... 

// 다른 패키지의 Child 클래스
public class Child extends Parent {
	
	public void call() {
		
		publicValue = 1; // public 멤버 변수 접근 가능
		protectedValue = 1; // protected 멤버 변수 접근 가능
		defaultValue = 1; // default 접근 불가! => 컴파일 에러
		protectedValue = 1; // protected 접근 불가! => 컴파일 에러

		publicMethod(); // public 메서드 접근 가능
		protectedMethod(); // protected 메서드 접근 가능
		defaultMethod(); // default 접근 불가! => 컴파일 에러
		protectedMethod(); // protected 접근 불가! => 컴파일 에러
	}
}

 

📌 protected 접근 제어자 ⇒ 상속 관계 or 같은 패키지 접근 허용!

 

1-4. super

  • super 키워드 : 부모 참조
  • 자식과 부모의 필드 명이 같거나 메서드가 오버라이드 되어 있으면
    ⇒ 부모의 필드나 메서드 접근 불가 ⇒ 부모 참조 위해서 super 사용!
public class Parent {
	public String value = "parent";
	
	public void printValue() {
		System.out.println("Parent.value = " + value);
	}
}

...

public class Child extends Parent {
	public String value = "child";
	
	@Override
	public void printValue() {
		System.out.println("Child.value = " + value);
	}
	
	public void call() {
		System.out.println("this.value = " + this.value);
		// 부모의 필드 (value) 접근
		System.out.println("super.value = " + super.value);
		
		this.printValue();
		// 부모의 메서드 (printValue()) 접근
		super.printValue();
	}
}
public class Main {
	public static void main(String[] args) {
		Child child = new Child();
		child.call();
	}
}

/* 실행결과
this.value = child
super.value = parent
Child.value = child
Parent.value = parent
*/

 

1-5. super()

  • 자식 클래스의 인스턴스 생성 시 자식 뿐만 아니라 부모도 생성됨 (https://blogan99.tistory.com/113)
  • 따라서 각각의 생성자가 모두 호출되어야 함!!
📌 상속 관계 규칙

자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다!!
  • super(...) : 부모 생성자 호출
    • 부모의 기본 생성자 호출 ( super() )은 생략 가능함
  • 항상 생성자의 첫 줄에 작성해야 함!
    • this(...) 와 함께 사용할 시 한 번은 super(...) 를 호출해야 함!
      = 생성자 첫줄엔 this() 혹은 super() 를 작성해야 하는데 한번은 super()를 호출해야 함
public class ClassA {

    public ClassA() {
        System.out.println("ClassA 기본생성자");
    }
}


public class ClassB extends ClassA{

    public ClassB(int a) {
        // 부모의 기본 생성자 호출 생략 가능
        // super();
        System.out.println("ClassB 생성자 : a=" + a);
    }

    public ClassB(int a, int b) {
        // 부모의 기본 생성자 호출 생략 가능
        // super();
        System.out.println("ClassB 생성자 : a=" + a + " b=" + b);
    }
}


public class ClassC extends ClassB{

    public ClassC() {
        // 부모 클래스 (ClassB) 에 기본 생성자가 없기 때문에
        // 직접 부모 클래스의 생성자를 호출 해야 한다!!
        // super(); 사용 불가

        super(10); // 부모의 생성자 직접 호출
        System.out.println("ClassC 기본생성자");
    }
}


public class ClassMain {

    public static void main(String[] args) {

        ClassC classC = new ClassC();

    }
}

/* 실행결과
ClassA 기본생성자
ClassB 생성자 : a=10
ClassC 기본생성자  */