본문 바로가기

Java/문법

10-3장 메서드 오버라이딩

1. 메서드 오버라이딩의 개념과 동작

 

 1) 메서드 오버라이딩 (overriding)

 

  - 부모 클래스에게 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것

  - 부모의 메서드를 자신이 만든 메서드로 덮어씀

  - 조건 

    ● 부모 클래스의 메서드와 시그너치 및 리턴 타입이 동일해야 함

    ● 부모 클래스의 메서드보다 접근지정자의 범위가 같거나 넓어야 함

 

 2) 클래스 A와 B의 상속관계 및 print() 메서드의 오버라이딩

class A {
	void print() {
    	System.out.println("A 클래스");
     }
}

class B extends A {
	void print() { //print() 메서드 재정의 -> 오버라이딩
    	System.out.pritnln("B클래스");
}

  ●  A aa = new A() 

   - aa.print() ; -> A클래스 

 

 - 힙 메모리에 A() 생성자로 객체가 생성, 이를 A타입으로 선언한 참조 변수 aa는 이 객체를 가리킴 

 - 객체 내의 메서드 : 실제 메서드의 위치 정보 저장

 - 실제 print()는 메서드 영역에 정의 

 

●  B bb = new B()

 - bb.print(); -> B클래스 

 

 - 부모 클래스인 객체(A 객체)가 힙 메모리에 먼저 생성되므로 A 객체 내의 print()가 메서드 영역에 생성 

 - B 객체의 print() 역시 메서드 영역에 저장  -> 이미 A 객체를 생성하는 과정에서 print()메서드가 존재하고 있는 상황

 - B 객체의 print() 메서드가 이미 있는 A객체의 print()를 오버라이딩 

 - 메서드 영역의 print()메서드는 이미 B의 print() 메서드로 오버라이딩된 이후이므로 "B클래스"출력

 

●  A ab = new B ()

 - ab.print(); -> B 클래스 

 - B() 생성자로 객체 생성하고 A타입으로 참조 변수 선언 

 - 부모 클래스 A객체가 만들어지고, 이후 B의 객체가 만들어짐 

 - 메서드 영역에서는 print() 메서드의 오버라이딩 발생

 - A 타입의 참조 변수를 사용하고 있으므로 참조변수는 A객체를 가리키고 있음

 - B의 print()로 오버라이딩된 이후이므로 A 객체 내부의 print()가 실행됐는데도 "B 클래스"출력

 

3. 메서드 오버라이딩을 사용하는 이유 

 

class Animal {
	void cry() {
    }
}

class Bird extends Animal {
	void cry() {
    	System.out.println("짹짹");
    }
}

class Cat extends Animal {
	void cry() {
    	System.out.println("야옹");
    }
}

class Dog extends Animal {
	void cry() {
    	System.out.println("멍멍");
    }
}

 - 4개의 클래스 모두 cry() 메서드를 포함 

 

 - 각각의 타입으로 선언 + 각각의 타입으로 객체 생성 

Animal aa = new Animal();
Bird bb = new Bird();
Cat cc = new Cat();
Dog dd = new Dog();

aa.cry(); //출력없음
bb.cry(); //짹짹
cc.cry(); //야옹
dd.cry(); //멍멍

 - 부모 클래스 타입으로 선언 + 각각의 타입으로 객체 생성

Animal ab = new Bird(); 
Animal ac = new Cat();
Animal ad = new Dog();

ab.cry(); //짹쨱
ac.cry(); //야옹
ad.cry(); //멍멍

 - 참조 변수 ab,ac,ad는 모두 Animal 타입이지만 , 각각 서로 다른 메서드로 오버라이딩 -> cry() 메서드는 서로 다른 출력

 - 다형적 표현으로 자식 클래스들의 객체를 부모 클래스인 Animal 타입으로 선언할 수 있음 -> Animal 내부의 메서드만 사용 가능

 - Animal 클래스 내부의 cry()메서드가 없었다면 모든 참조 변수 cry() 호출 불가 

 - 부모 클래스 에 선언한 cry() 메서드 용도 : 자식 클래스에서 호출하기 위해서

 

- 배열로 한번에 관리 가능

Animal[] animals = new Animal[] {new Bird(), new Cat(), new Dog() };
for(Animal animal : animals) {
	animal.cry();
} //쨱짹, 야옹, 멍멍

 

3. 메서드 오버라이딩과 메서드 오버로딩

 

 1) 오버로딩(overloading)

  - 이름이 동일하지만, 시그니처가 다른 여러 개의 메서드를 같은 공간에 정의

  - 파일명은 동일하지만, 확장명이 다른 파일(abc.jpg, abc.png,...)을 같은 폴더에 복사 

 

 2) 오버라이딩(overriding)

  - 파일명과 확장명이 완벽하게 동일한 파일을 같은 공간에 복사

 

package Inheritance;

class A {
	void print1() {
		System.out.println("A 클래스 print1");
	}
	void print2() {
		System.out.println("A 클래스 print2");
	}
}

class B extends A {
	@Override
	void print1() {
		System.out.println("B 클래스 print1");
	}
	void print2(int a) {
		System.out.println("B 클래스 print2");
	}
}
public class MethodOverriding {
	public static void main(String[] args) {
		//A 타입 선언 / A 생성자 사용
		A aa = new A(); 
		aa.print1();
		aa.print2();
		System.out.println();
		
		//B 타입 선언/ B 생성자 사용
		B bb = new B();
		bb.print1();
		bb.print2();
		bb.print2(3);
		System.out.println();
		
		//A 타입 선언/B 생성자 사용
		A ab = new B();
		ab.print1();
		ab.print2();
	}
}

 - 클래스 A : print1() , print2() 메서드 

 - 클래스 B : 클래스 A를 상속 , print1()와 print2(int a) 를 추가로 정의 

 

 - print1()은 상속받은 메서드와 리턴 타입과 시그니처가 완벽하게 동일하므로 오버라이딩

 - A에게 상속받은 print2() : 입력매개변수가 없음

   B에서 추가로 정의한 print2(int a) : int형 입력매개변수 존재 

   => 메서드 시그니처가 다름 (오버로딩)

 

 - 클래스 B 내부에서는 print1(), print2(), print2(int a)메서드 사용 가능

   

4. 메서드 오버라이딩과 접근 지정자 

 

 - 자식 클래스가 부모 클래스의 메서드를 오버라이딩할 때는 접근지정자의 범위를 좁힐 수 없음

 - 메서드 오버라이딩할 때 사용할 수 있는 접근 지정자 

부모 클래스 메서드의 접근지정자 메서드 오버라이딩을 할 떄 사용할 수 있는 접근지정자
public public
protected public, protected
default public, protected, default
private public, protected, default, private