java 상속

개미Coder
|2024. 4. 23. 10:59
반응형

## 상속 개념

 

상속(Inheritance)이란?

-현실 세계: 부모가 자식에게 물려주는 행위

-객체 지향 프로그램 : 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것

자식이 부모를 선택해 물려받음, 상속 대상 : 부모의 필드와 메소드

 

#상속의 효과

-부모 클래스 재사용해 자식 클래스 빨리 개발 가능

-반복된 코드 중복 줄임

-유지 보수 편리성 제공

-객체 다형성 구현 가능 (주목적)

 


 

#상속 대상 제한

-부모 클래스의 private 접근 갖는 필드와 메소드 제외(protected 상속받은 대상은 접근 가능)

-부모 클래스가 다른 패키지에 있을 경우, default 접근 갖는 필드와 메서드도 제외

 

현실에서 상속(Inheritance)은 부모가 자식에게 물려주는 행위를 말한다. 
자식은 상속을 통해서 부모가 물려준 것을 자연스럽게 이용할 수 있다. 
객체 지향 프로그램에서도 부모 클래스의 멤버를 자식 클래스에게 물려줄 수 있다. 
프로그램에서는 부모 클래스를 상위 클래스라고 부르기도 하고, 자식 클래스를 하위 클래스, 또는 파생 클래스라고 부른다.

상속은 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.
fieldl, field2, method1() , method2()를 가지는클래스를 작성한다고 생각해보자.
4개를 모두 처음부터 작성하는 것보다는 field1 과 method1()을 가지고 있는 클래스가 있다면 이것을 상속하고,
field2와 method2()만 추가 작성하는 것이 보다 효율적이고 개발시간을 절약시켜준다.

실제로 B 클래스를 객체 생성해서 다음과 같이 사용할 때에는 
마치 B가 field1과 method1()을 가지고 있는 것처럼 보인다.

// A로부터 물려받은 필드와 메서드
B b = new B() ; 
b.field1 = 10; 
b.method1();

// B가 추가한 필드와 메서드
b.field2 = "홍길동";
method2();




상속을 해도 부모 클래스의 모든 필드와 메소드들을 물려받는 것은 아니다. 
부모 클래스에서 private접근 제한을 갖는 필드와 메소드는 상속 대상에서 제외된다. 
그리고 부모 클래스와 자식 클래스가 다른 패키지에 존재한다면 
default 접근 제한을 갖는 필드와 메소드도 상속 대상에서 제외된다. 
그 이외의 경우는 모두상속의 대상이 된다.

상속을 이용하면 클래스의 수정을 최소화시 킬 수도 있다. 
부모 클래스의 수정으로 모든 자식 클래스들의 수정 효과를 가져오기 때문에 유지 보수 시간을 최소화시켜준다. 
예를 들어 클래스 B, C가 클래스 A를 상속할 경우 A의 필드와 메소드를 수정함으로써 
B, C를 수정하지 않아도 수정된 A의 필드와 메소드를 이용할 수 있다.






 

 

# 클래스 상속

 




현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다. 
자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지를 결정하고 
선택된 부모 클래스는 다음과 같이 extends를 뒤에 기술한다.

class 자식클래스 extends  부모클래스 {
// 필드 
// 생성자 
// 메소드
}




예를 들어 Car 클래스를상속해서 SportsCar 클래스를 설계하고 싶다면 다음과 같이 작성하면 된다.

class SportsCar extends  Car  {
}





다른언어와는달리 자바는다중상속을허용하지 않는다. 
즉여러 개의 부모클래스를상속할수 없다. 
그러므로 다음과 같이 extends 뒤에는 단 하나의 부모 클래스만 와야 한다.

class 자식클래스 extends  부모클래스1, 부모클래스1 {
}

 

 


 

 

 

-부모와 자식의 상속 (extends를 사용한 클래스 상속)

-animal(부모) dog(자식)에 대한 예시

 

 

 


 

 

-생성자와 메서드에 대한 구분

-생성자는 클래스를 사용하기 위해 객체를 생성하는데 그때 받아올 값을 정해주는 것

(예를 들면 String, int 여러 타입 등등..)

-메서드는 실행값이다. 무얼무얼 실행하겠다. 함수같은..

 

아래는 핸드폰(부모) -> 스마트폰(자식) 클래스 상속 예제이다.

여전히 부모 클래스(CellPhone)에 extends를 사용하면 자식 클래스 (SmartPhone)에서 생성자를 호출해서 부모 클래스의 메서드와 필드 등을 사용할 수 있다.

 

생성자 참고

 

package example0423.chapter07.example01;

public class SmartPhone extends CellPhone {
	// 필드
	int channel;
	String search;
	
	// 생성자 (String model과 String color는 상속받았다. 부모 필드)
	SmartPhone(String model, String color, int channel) {
		this.model = model;	// 부모인 cellPhone으로부터 상속
		this.color = color;	// 부모인 cellPhone으로부터 상속
		this.channel = channel;
	}
	
	SmartPhone(String model, String color, String search) {
		this.model = model;	// 부모인 cellPhone으로부터 상속
		this.color = color;	// 부모인 cellPhone으로부터 상속
		this.search = search;
	}
	 
	// 메서드 (DMB, 검색)
	void turnOnDmb() {		// 채널만 호출하면 채널만 입력됨
		System.out.println("채널" + this.channel + "번의 방송 수신을 시작합니다.");
	}
	
	void changeDmb(int channel) {	//매개변수를 넣어서 호출하면 필드값에 저장되고 출력됨
		this.channel = channel;
		System.out.println("채널" + this.channel + "번의 방송 수신을 시작합니다.");
	}
	
	void turnOffDmb() {
		System.out.println("DMB 방송을 종료합니다.");
	}
	
	void search(String search) {
		this.search = search;
		System.out.println("입력하신" + search + "를 검색합니다.");
	}
	
}
package example0423.chapter07.example01;

public class CellPhone {

	
	// 필드
	String model;
	String color;
	
	// 생성자 (Constructor)
	
	// 메서드
	void powerOn() {
		System.out.println("전원을 켭니다.");
	}
	void powerOff() {
		System.out.println("전원을 끕니다.");
	}
	void bell() {
		System.out.println("벨이 울립니다.");
	}
	void sendVoice(String message) {
		System.out.println("본인 : " + message);
	}
	void receiveVoice(String message) {
		System.out.println("상대방 : " + message);
	}
	void hangUp() {
		System.out.println("전화를 끊습니다.");
	}
	
	
}
package example0423.chapter07.example01;

public class PhoneMain {
	
	public static void main(String[] args) {
		
		// 이때 생성자가 매개변수를 받기 때문에 받지 않고 생성하면 오류가 뜬다.
		SmartPhone sp = new SmartPhone("갤럭시", "검정", 35);
		
		System.out.println("모델명 : " + sp.model);
		System.out.println("색상 : " + sp.color);
		System.out.println("채널 : " + sp.channel);
		System.out.println("검색 : " + sp.search);
		
		
		// 부모 method
		sp.powerOn();
		sp.bell();
		sp.sendVoice("모시모시");
		sp.receiveVoice("홍길동씨 번호인가요?");
		sp.sendVoice("잘못거셨습니다.");
		sp.hangUp();
		
		System.out.println();
		
		// SmartPhone 자신의 method 호출
		sp.changeDmb(50);
		sp.turnOffDmb();
		sp.search("NAVER");
		
	}

}

 

왼쪽 부모 클래스 / 오른쪽 자식 클래스

 

 

부모 클래스와 자신의 클래스에서 만든 메서드와 필드값을 자식 클래스의 생성자를 호출해서 사용했다.

 

※ 여기서 알아둬야 할 것은 자식이 상속받은 필드값을 저장했다고, 부모 필드값에 그 값이 저장되는 것은 아니다.

CellPhone cp = new CellPhone();

System.out.println(cp.color);	// null

위쪽에서 분명 color를 검정색이라고 생성자(Constructor)에서 지정을 해줬는데 값은 null이라고 찍힌다.

 

 

 

 

 

 


 

 

 

 

#부모 생성자 호출(super(...))

 

 

-명시적인 부모 생성자 호출

-부모 객체 생성할 때, 부모 생성자 선택해 호출

 

super(매개값,...) -> 부모 객체에 매개변수가 있으면 매개값을 넣어줘야 한다.

 

-매개값과 동일한 타입, 개수, 순서 맞는 부모 생성자 호출

-부모 생성자 없다면 컴파일 오류 발생 (생성자를 안만들어도 기본 생성자를 컴파일 할 때 자동으로 만들어준다)

-반드시 자식 생성자의 첫 줄에 위치

-부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수 작성

 

 

 

-부모 생성자가 기본적으로 생성자를 만들지 않았다면, 컴파일러에서 기본적으로 생성자를 자동으로 만든다.

 

super(); 라는 부모 생성자가 자동으로 들어가 있다 (열일하는 컴파일러)

 

 

 

#생성자 만들기 예시

부모 생성자
자식 생성자
이렇게 불러서 사용한다.

 

-대표적으로 자식 클래스에서 부모 상속자를 부여받을 때 사용하는 방법이다.

	// 생성자1
	Student(int studentNo) {
		super("홍길동", "123456-1234567");
		this.studentNo = studentNo;
	}
	
	// 생성자 2
	Student(String name, String ssn, int studentNo) {
		super(name, ssn);
		System.out.println("Student 생성자 호출");
		this.studentNo = studentNo;
	}



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

아래 코드는 SmartPhone 객체만 생성하는 것처럼 보이지만, 
사실은 내부적으로 부모인 CellPhone 객체가 먼저 생성되고 SmartPhone 객체가 생성된다.


SmartPhone smartPhone = new SmartPhone();


이것을 메모리로 표현하면 다음과 같다.
                          
모든 객체는 클래스의 생성자를 호출해야만 생성된다. 부모 객체도 예외는 아니다. 
그렇다면 부모객체를 생성하기 위해 부모 생성자를 어디서 호출한 것일까? 
이것에 대한 비밀은 자식 생성자에 숨어 있다. 부모 생성자는 자식 생성자의 맨 첫 줄에서 호출된다. 
예를 들어 SmartPhone의 생성자가 명시적으로 선언되지 않았다면 
컴파일러는 다음과 같은 기본 생성자를 생성해 낸다.

public smartPhone () {
super(); // 부모의 기본 생성자를 호출하는 코드 (개발자가 생략시 컴파일러가 생성한다.)
}

첫 줄에 super ();가 추가된 것을 볼 수 있다. 
super() 는 부모의 기본 생성자를 호출한다. 즉 CellPhone 클래스의 다음 생성자를 호출한다.

아래는 부모의 기본 생성자이다.

public CellPhone()  (
}






CellPhone.java 소스 코드에서도 CellPhone의 생성자가 선언되지 않았지만 
컴파일러에 의해 기본 생성자가 만들어지므로 문제없이 실행된다. 
만약 직접 자식 생성자를 선언하고 명시적으로 부모 생성자를 호출하고 싶다면 다음과 같이 작성하면 된다.


자식클래스명(매개변수, ...)  { // 자식 클래스의 생성자
super(매개값, ...); // 개발자가 생략시 컴파일러가 생성한다.
}


*super(매개값, ...)는 매개값의 타입과 일치하는 부모 생성자를 호출한다. 
*만약 매개값의 타입과 일치하는 부모 생성자가 없을 경우 컴파일 오류가 발생한다.
*super(매개값, ... )가 생략되면 컴파일러에 의해 super()가 
 자동적으로 추가되기 때문에 부모의 기본 생성자가 존재해야 한다.   
*부모 클래스에 기본 생성자가 없고 매개 변수가 있는 생성자만 있다면 
 자식 생성자에서 반드시 부모 생성자 호출을 위해 super(매개값, ...)를 명시적으로 호출해야 한다. 
*super(매개값, .. .)는 반드시 자식 생성자 첫 줄에 위치해야 한다. 그렇지 않으면 컴파일 에러가 난다. 

 


 

기본 상속자가 없으면 상속받은 자식 클래스는 컴파일 오류가 발생

 

 


예제 발생 예제 발생

package example0423.chapter07.example02;

public class People {

	public String name;
	public String ssn;
	
	public People(String name, String ssn) {
		System.out.println("People 생성자 호출");
		this.name = name;
		this.ssn = ssn;
	}
	
}
package example0423.chapter07.example02;

public class Student extends People {

	public int studentNo;
	
	// 생성자1
	Student(int studentNo) {
		super("홍길동", "123456-1234567");
		this.studentNo = studentNo;
	}
	
	Student(String name, String ssn, int studentNo) {
		super(name, ssn);
		System.out.println("Student 생성자 호출");
		this.studentNo = studentNo;
	}
	
}
package example0423.chapter07.example02;

public class StudentMain {

	public static void main(String[] args) {

		Student std = new Student("홍길동", "123456-1234567", 423);
		
		System.out.println("이름 : " + std.name);
		System.out.println("주민등록번호 : " + std.ssn);
		System.out.println("학번 : " + std.studentNo);
		
	}
}

 

 

 

 

 

 

 

 

 

 

 

-자식 클래스에서 만든 메서드를 통해서 부모 클래스에 있는 필드값과 자식 클래스의 필드값 변경하기

항상 상속 클래스를 지정해주려면 super를 붙여주자구

 

so cool

 

 

 

 


 

 

 

 

## 메소드 재정의(Override)

 

 

 

-자식 클래스 입장에서 부모 클래스의 메소드를 수정해서 사용하는 것

-오버로딩과는 구분하자


부모 클래스의 모든 메소드가 자식 클래스에게 맞게 설계되어 있다면 가장 이상적인 상속이지만,
어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수도 있다. 

이 경우 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용해야 한다. 
자바는 이런 경우를 위해 메소드 오버라이딩(Overriding) 기능을 제공한다.


# 메소드 재정의(@Override)

메소드 오버라이딩은 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우,
자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다.    
메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 
자식 객체에서 메소드를 호출하면 오벼라이딩된 자식 메소드가 호출된다.


메소드를 오버라이딩할 때는 다음과 같은 규칙에 주의해서 작성해야 한다.
•부모의 메소드와 동일한 시그너처(메소드 이름, 매개 변수 리스트(개수, 타입, 순서), 리턴 타입)를 가져야 한다.
•접근 제한을 더 강하게 오버라이딩할 수 없다.
•새로운 예외(Exception)를 throws 할 수 없다(예외는 뒤에서...)

접근 제한을 더 강하게 오버라이딩할 수 없다는 것은 부모 메소드가 public 접근 제한을 가지고 있 을 경우 
오버라이딩하는 자식 메소드는 default나 private 접근 제한으로 수정할 수 없다는 뜻이다. 

반대는 가능하다. 부모 메소드가 default 접근 제한을 가지면 재정의되는 자식 메소드는 
default 또는 public 접근 제한을 가질 수 있다. 

 

이 두개를 보면 뭐가 뭘 상속받았는지 모른다.
이 알짜배기 친구를 넣으면 이제 분간이 가능해진다. 부모로 부터 상속받은 메서드군.
Source -> Override/Implement Methods 이렇게 사용도 가능하다네.

 

 

 

-Override에 대한 예제


 

 

-Override 예제

 

 

 

 

# 부모 메소드 호출 (super) 

 


자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면, 
부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용된다. 
그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 
명시적으로 super 키워드를 붙여서 부모 메소드를 호출할수 있다. 
super는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있다.

-this  = 객체 자신을 의미한다.
-super = 상속에서 부모 객체를 의미한다.

super.부모메서드(); // 자식이 부모의 메서드를 호출시 사용한다.

 

 

 

 

 

 

 

 


 

 

 

 

#  final 클래스와 final 메소드

 

 

-final 필드 : 수정 불가 필드

-final 클래스 : 부모로 사용 불가한 클래스

-final 메소드 : 자식이 재정의할 수 없는 메소드


-final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있다. 
-final 키워드는 해당 선언이 최종 상태이고, 결코 수정될 수 없음을 뜻한다. 
-final 키워드가 클래스, 필드, 메소드 선언에 사용될 경우 해석이 조금씩 달라진다. 
-필드 선언 시에 final이 지정되면 초기값 설정 후, 더 이상 값을 변경할 수 없다는 것을 알았다. 
-그렇다면 클래스와 메소드에 final 이 지정되면 어떤 효과가 날까? 
-클래스와 메소드 선언 시에 final 키워드가 지정되면 상속과 관련이 있다.

# 상속할 수 없는 final 클래스

-클래스를 선언할 때 final 키워드를 class 앞에 붙이게 되면 
 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다. 
 즉 final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다는 것이다.

public final class 클래스 { ... }



final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스이다. 
String 클래스는 다음과 같이 선언되어 있다. 그래서 자식 클래스를 만들 수 없다.

public final class String { ..  }

 

상속이 잘 되었었는데,
class 앞에 final을 넣으니 상속이 되지 않아 오류가 뜬다

 

 

 

 

 

#Protected 접근 제한자

 

 

 

-다른 pakage여도 extends 받은 자식 클래스는 접근이 가능하다.

반면, 다른 pakage에 저장되어 있는 클래스는 생성이 안된다.
반면, extends된 클래스는 호출이 가능하다.

 

 

반응형

'java(2)↗' 카테고리의 다른 글

java 인터페이스  (0) 2024.04.24
java 다형성  (0) 2024.04.23
java 클래스(2)  (0) 2024.04.22
java 클래스  (0) 2024.04.19
java 데이터 참조 타입  (0) 2024.04.18