티스토리 뷰

Java

Java(상속, 오버라이드)

yoooon1212 2024. 4. 24. 16:40

 

<상속>

기존의 클래스를 확장하여 새로운 클래스를 정의하는 매커니즘

기존 클래스의 속성과 메서드를 재사용하고 확장하여 새로운 클래스를 만들 수 있음.

 

상속에서는 두 개의 클래스가 필요함

부모 클래스(슈퍼 클래스): 기존에 정의된 클래스로, 상속의 대상이 되는 클래스
(부모 클래스의 속성과 메서드를 자식 클래스에게 상속함)

자식 클래스(서브 클래스): 부모 클래스를 확장하여 새롭게 정의되는 클래스
(부모 클래스의 모든 속성과 메서드를 상속 받음)

 

 

 

 

 

예시 

public class A {

String name;

int height;

int weight;

int age;

}

 

class B {   // 접근제어 지시자 : default

String name;

int height;

int weight;

int age;

 

int level;

String nickName;

}

 

// 상속할 때 extends 사용(A가 부모, C가 자식/ C가 A로부터 상속 받은 후 기능적으로 확장되서 크기가 크다.)

class C extends A {  // 클래스 C가 A로부터 상속 받음

int level;

String nickName;

String phone;

}

하나의 자바 파일 안에 여러 개의 클래스를 작성할 수 있다 

단, public 이 붙은 클래스는 오직 하나만 정의 가능하다

 

public class CTest {

 

public static void main(String[] args) {

C c = new C(); // A의 상속을 받은 C 의 객체를 생성

c.name = "A"; // 객체의 변수명을 통해 객체에 접근함. C는 A의 상속을 받아서 .name은 A의 name이다.

System.out.println(c.name); //A가 출력됨

} // end of main

} // end of class

 

 

 

 

부모 생성자 호출

  • 자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다.
  • 모든 객체는 생성자를 호출해야만 생성된다.
  • 부모 생성자는 자식 생성자의 super()에 의해 호출된다. (super()는 컴파일 과정에서 자동 추가된다. 이는 부모의 기본 생성자를 호출한다.)
  • 부모 클래스에 기본 생성자가 있다면 super(); 는 생략 가능하다.
  • 만약 부모의 클래스에 기본 생성자가 없다면 자식 생성자 선언에서 컴파일 에러가 발생한다.
  • 부모 클래스에 기본 생성자가 없고 매개변수를 갖는 생성자만 있다면 super(매개값, ...) 코드를 반드시 작성해야 한다.
// 자식 생성자 선언
public 자식 클래스(...){
super();
...
}

public 자식 클래스(...){
super(매개값, ...);
...
}

 

 

자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다.

=> 이 부분을 좀 더 알아보자

자식클래스 변수 = new 자식클래스();

이 코드는 자식클래스 객체만 생성된 것처럼 보이지만, 사실은 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된 것이다.

 

 

 

 

 

 

<오버라이드>

상속관계에서 부모 클래스에 정의된 메서드를 자식 클래스에서 재정의(다시 정의)하는 것

(이유: 부모 클래스의 메서드가 자식 클래스가 사용하기에 적합하지 않기 때문)

자식 클래스에서 부모 클래스의 메서드를 새로운 내용으로 구현하는 것

=> 자식 클래스는 부모 클래스의 메서드를 덮어쓰게 되어(숨겨짐) 부모 클래스의 메서드

대신 자식 클래스에서 정의한 메서드가 우선적으로 호출됨.

 

메서드 오버라이딩 시 규칙
  • 부모 메소드의 선언부(리턴 타입, 메서드 이름, 매개변수)와 동일해야 한다.
  • 접근 제한을 더 강하게 오버라이딩 할 수 없다(public -> private으로 변경 불가)
  • 새로운 예외를 throws 할 수 없다

 

 

 

예시1)

public class Cal {

 

public Cal() {

System.out.println("Cal() 부모 생성자 호출");

}

 

public int sum(int n1, int n2) {

return n1 + n2;

}

 

public int multiply(int n1, int n2) {

System.out.println("2. 호출");

System.out.println("여기는 부모 클래스 메서드 입니다.");

return n1 * n2;

}

 

// 코드 테스트

public static void main(String[] args) {

Cal2 cal2 = new Cal2(); // 메모리에 자식(cal2)만 올림

// heap 메모리에 먼저 부모 클래스가 태어난 후 자동으로 자식이 올라옴.

System.out.println(cal2.sum(5, 3));

System.out.println(cal2.minus(10, 3));

System.out.println(cal2.multiply(10, 0));

}

}

 

class Cal2 extends Cal {

 

public Cal2() {

System.out.println("Cal2() 자식 생성자 호출");

}

 

public int minus(int n1, int n2) {

return n1 - n2;

}

@Override // <- 오버라이드된 메서드다 

public int multiply(int n1, int n2) {

System.out.println("1. 호출");

return super.multiply(n1, n2); //this 대신에 super.(부모 클래스의 객체)로 부모의 메서드를 호출함

}

 

class Cal에 메인 함수가 있으며 코드 테스트 한 결과

 

Cal2만 객체를 생성하였지만 출력은 Cal() 부모 생성자 호출이 먼저 출력된 후 Cal2() 자식 생성자 호출이 출력된다. 

이는 heap 메모리에 먼저 부모 클래스가 태어난 후 자동으로 자식이 올라와서 이다. 

 

Cal을 상속받은 Cal2가 Cal 의 sum 메서드를 호출하여 출력하면 8(5+3)이 출력된다.

다음으로 Cal2가 minus 메서드를 호출하여 7(10-3)이 출력된다.

 

Cal2의 multiply 메서드를 호출하면 Cald의 메서드가 아닌 오버라이드된 Cal2 의 메서드가 호출된다.

그러므로 1. 호출이 먼저 출력되고, return 값으로 super(Cal)의 multifly 메서드가 호출되어 2. 호출이 출력되고

return 값 0(10*0)이 출력된다.

 

추가로 Cal2 가 Cal 로부터 상속받음으로써 Cal2클래스는 Cal 클래스라고도 할 수 있다. 이를 다형성이라고 한다.

 

 

 

예시2)

부모 클래스 생성

public class Hero {

 

String name;

int hp;

 

public Hero() {

} // 부모 클래스가 메모리에 먼저 떠야 함. -> 자식 생성자 먼저 만들어야 함.

 

public Hero(String name, int hp) {

this.name = name;

this.hp = hp;

}

 

void attack() {}

}

 

 

자식 클래스(Warrior) 생성

public class Warrior extends Hero{

 

public Warrior(String name, int hp) { // 호출한 내용을 받을 수 있도록 매개변수 똑같이 입력하기

super(name, hp); // super로 부모 생성자 호출

}

 

// 오버라이드

@Override

void attack() {

System.out.println("전사가 기본 공격을 합니다.");

}

 

void comboAttack() {

System.out.println("전사가 2단 공격을 합니다.");

}

}

 

자식 클래스(Archer) 생성

public class Archer extends Hero {

 

public Archer(String name, int hp) {

super(name, hp);

}

 

@Override

void attack() {

System.out.println("궁수가 기본 공력을 합니다.");

}

 

void fireArrow() {

System.out.println(name + " 가 불화살 공격을 합니다");

}

}

 

자식 클래스(Wizard) 생성

public class Wizard extends Hero {

 

public Wizard(String name, int hp) {

super(name, hp); // 부모 생성자 호출

}

 

@Override

void attack() {

System.out.println("마법사가 기본 공격을 합니다.");

}

 

void freezing() {

System.out.println("마법사가 얼음 공격을 합니다");

}

}

 

테스트 파일 생성

public class HeroTest {

 

public static void main(String[] args) {

 

Hero hero = new Hero("영웅", 100);

Warrior warrior1 = new Warrior("야스오", 100);

Archer archer1 = new Archer("애쉬", 100);

Wizard wizard = new Wizard("라이즐", 100);

 

warrior1.attack();

}

}

 

부모 메서드 호출

메서드를 재정의하면, 부모 메서드는 숨겨지고 자식 메서드만 사용되기 때문에 비록 부모 메서드의 일부만 변경된다 하더라도 중복된 내용을 자식 메서드도 가지고 있어야 한다. 이 문제는 공동 작업 처리 기법을 이용하면 해결된다.

자식 메서드 내에서 부모 메서드를 호출하는 방법이며 super 키워드 . 연산자를 사용하면 숨겨진 부모 메서드를 호출 할 수 있다. (부모 메서드를 재사용함으로써 자식 메서드의 중복 작업 내용을 없애는 효과를 가져온다.)

 

class Parent{
public void method() {
// 작업 처리 1
}
}
class Child extends Parent{

// 재정의된 메서드
@Override
public void method() {
super.method(); // 부모(Parent class)의 method() 메서드 호출
// 작업 처리2
}
}

 

 

 

오버로드와 오버라이드의 차이는?

오버로드(Overload): 메서드의 이름은 같고 파라메터의 개수, 타입, 순서가 다른 함수를 정의하는 것(리턴값만을 다르게 갖는 오버로드는 작성 할 수 없다.)
오버라이드(Override): 상위 클래스(부모 클래스)의 메서드를 재정의 하는 것. 메서드의 이름, 파라메터의 개수, 타입, 순서가 동일해야 하며, 부모 클래스의 동작을 상속받은 하위 클래스(자식 클래스)에서 변경하기 위해 사용된다.

 

즉, 오버로딩은 새로운 메서드를 정의하는 것이고, 오버라이딩은 상속 받은 메서드의 내용만 변경 하는 것이다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함