티스토리 뷰

728x90
반응형

이전 게시물에 이어 이번에도 상속에 대해 다뤄보려 한다. 이전 게시물은 아래 링크!

 

https://live-for-myself.tistory.com/89

 

[Java] 상속 Inheritance #1 / extends, super, 다형성(polymorphism), 상속에서의 casting

자바에는 '상속'이라는 개념이 있다.  상속은, 자바의 중요한 특성들 중 하나이며 상속을 사용하는 이유는 좋은 프로그램을 만들기 위해서이다. 자식 클래스는, 상속을 받고싶은 부모 클래스를

live-for-myself.tistory.com


1) Override

 

저번 시간에 다룬 핸드폰 클래스에 사진찍는 기능을 추가해보자.

 

HandPhone.java - 30만 화소의 사진을 찍는 takePicture() 메소드 추가

package test.mypac;
//Phone 클래스를 확장해서 HandPhone 클래스를 정의한다.
//Phone 클래스를 상속 받아서 자식 클래스 HandPhone을 정의한다.
public class HandPhone extends Phone{
	
	//이동 중에 전화를 거는 메소드
	public void mobileCall() {
		System.out.println("이동 중에 전화를 걸어요!");
	}
	
	//사진 찍는 메소드
	public void takePicture() {
		System.out.println("30만 화소의 사진을 찍어요");
	}
}

메인에서 핸드폰으로 사진을 찍어보자.

package test.main;

import test.mypac.HandPhone;

public class MainClass07 {
	public static void main(String[] args) {
		
		HandPhone p2 = new HandPhone();
		p2.takePicture();
		
	}
}

 

실행 결과는, "30만 화소의 사진을 찍어요" 가 콘솔창에 출력된다.

 

좀 더 높은 화소의 사진을 스마트폰으로 찍고싶다. 스마트폰 클래스에서 핸드폰에서 상속 받은 takePicture() 메소드를 수정할 수 있으면 좋을 것 같다.

 

이럴 때 사용하는 것이 "Override"이다. 

쉽게 말해 덮어쓰기 기능인데, 부모 클래스의 기능을 새로 작성할 수 있다. 

1000만 화소로 찍혔으면 좋겠으니 다음과 같이 수정해보자.


SmartPhone.java - Override로 화소 업그레이드 하기

package test.mypac;

public class SmartPhone extends HandPhone{
	
	public void doInternet(){
		System.out.println("인터넷을 해요!");
	}
	@Override //덮어쓰기
	public void takePicture() {
		// TODO Auto-generated method stub
		//필요하다면 부모의 원래 메소드도 여기서 super 예약어를 이용해서 호출할 수도 있다.
		//super.takePicture();
		System.out.println("1000만 화소의 사진을 찍어요");
	}
}

 

이제 메인에서 실행해보자.


MainClass07.java

package test.main;

import test.mypac.HandPhone;
import test.mypac.SmartPhone;

public class MainClass07 {
	public static void main(String[] args) {
		//스마트폰으로 사진을 찍어보자
		SmartPhone p1 = new SmartPhone();
		p1.takePicture();
		
		HandPhone p2 = new HandPhone();
		p2.takePicture();
		
	}
}

이 코드를 실행하면 "1000만 화소의 사진을 찍어요"가 콘솔창에 출력된다.

 

SmartPhone클래스에서 주석처리한

super.takePicture()을 코드에 넣어주면, 메인에서 p2.takePicture()을 할 시, 핸드폰 클래스의 takePicture()을 호출하기 때문에 "30만 화소의 사진을 찍어요"도 출력된다.


2) 자식 클래스 타입의 객체를 생성할 때 힙에서의 구조

 

그렇다면, 호출 구조를 정확히 확인해보기 위해, Phone, HandPhone, SmartPhone 클래스에 각각 생성자를 넣어보자.

 

Phone.java - 생성자 추가

package test.mypac;

public class Phone extends Object{
	public Phone() {
		// TODO Auto-generated constructor stub
		System.out.println("Phone() 생성자");
	}
	//전화 건는 메소드
	public void call() {
		System.out.println("전화를 걸어요");
	}

}

 

 

HandPhone.java - 생성자 추가

package test.mypac;
//Phone 클래스를 확장해서 HandPhone 클래스를 정의한다.
//Phone 클래스를 상속 받아서 자식 클래스 HandPhone을 정의한다.
public class HandPhone extends Phone{
	public HandPhone() {
		// TODO Auto-generated constructor stub
		System.out.println("HandPhone() 생성자");
	}
	//이동 중에 전화를 거는 메소드
	public void mobileCall() {
		System.out.println("이동 중에 전화를 걸어요!");
	}
	
	//사진 찍는 메소드
	public void takePicture() {
		System.out.println("30만 화소의 사진을 찍어요");
	}
}

 

 

SmartPhone.java - 생성자 추가

package test.mypac;

public class SmartPhone extends HandPhone{
	public SmartPhone() {
		super();//부모생성자 호출 , 안써도 알아서 호출됨. 생략되어 있음. 순서도 생성자 안에서 가장 먼저 해주어야 함.
		
		
		// TODO Auto-generated constructor stub
		System.out.println("SmartPhone 생성자");
	}
	public void doInternet(){
		System.out.println("인터넷을 해요!");
	}
	@Override //덮어쓰기
	public void takePicture() {
		// TODO Auto-generated method stub
		//필요하다면 부모의 원래 메소드도 여기서 super 예약어를 이용해서 호출할 수도 있다.
		//super.takePicture();
		System.out.println("1000만 화소의 사진을 찍어요");
	}
}

 

 

다시 MainClass07을 실행하면 다음과 같이 출력된다.

 

출력결과

 

 

스마트폰 객체를 생성하면, Phone, HandPhone, SmartPhone 생성자가 모두 호출되고, 

핸드폰 객체를 생성하면, Phone, HandPhone 생성자가 호출되는 모습이다.


 

 

 

따라서, 다음과 같은 구조라 생각해볼 수 있다.

 

중요!!!

핸드폰 객체를 생성할 때 부모 클래스인 Object와 Phone 객체가 모두 생성이 되고, 힙의 하나의 사물함에 담겨 같은 참조값으로 묶인다.

 

따라서, 지역변수 p에 참조값이 들어갈 때, 세 개의 객체가 들어있는 사물함의 참조값인 19가 들어간다. 

 

조금 어려울 수 있지만 결론적으로, 자식 클래스 타입의 객체가 생성되면 같은 공간에 부모 클래스 타입의 객체가 같이 생성된다는 말이다.

 

같은 사물함 안에서 관리가 되므로, 자식 클래스 타입의 객체에서 부모 클래스 타입의 객체의 필드와 메소드를 "this"를 통해서 참조할 수 있다.

 

 

 

 

 

위 예제에서 takePicture()메소드를 오버라이드했을 때의 모습은 다음과 같다.


마찬가지로 동일한 내용의 예제이다.

MainClass09.java

package test.main;

import test.mypac.SmartPhone;

public class MainClass09 {
	public static void main(String[] args) {
		new SmartPhone();
	}
}

 

출력 결과

 

또한, 부모 클래스의 생성자를 호출하고 싶을 때는 생성자 안에 super()을 작성한다.

작성하지 않아도 자동으로 호출된다.

작성 시에는 생성자 내부의 맨 첫줄에 작성해야 한다.

 


다음 결과가 어떨지 생각해보자.

 

MainClass08.java

package test.main;

import test.mypac.HandPhone;
import test.mypac.SmartPhone;

public class MainClass08 {
	public static void main(String[] args) {
		//HandPhone 객체를 생성해서 HandPhone type으로 받기
		HandPhone p1 = new HandPhone();
		//SmartPhone 객체를 생성해서 HandPhone type으로 받기
		HandPhone p2 = new SmartPhone();
		
		p1.takePicture();
		p2.takePicture();
		
	}

}

 

출력결과

 

언뜻 잘못 생각하면, p2.takePicture()이 핸드폰 타입의 지역변수에 담겨 있어 30만 화소로 찍힐 것 같지만, 사실 상 스마트폰 객체의 참조값이 들어있으므로 1000만 화소로 찍힌다. 

헷갈리지 않도록 주의해야 한다.

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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 31
글 보관함