자식 클래스는 멤버변수와 메서드를 물려받아 확장(extends)한다
class 자식클래스 extends 부모클래스 {
}
새로운 클래스를 정의 할 때
이미 구현된 클래스를 상속(inheritance) 받아서 속성이나 기능을 확장하여 클래스 구현
이미 구현된 클래스보다 더 구체적인 기능을 가진 클래스를 구현해야 할때 기존 클래스를 상속
여러 클래스에 동일한 코드 반복
→ 유지보수 어렵기 때문에 재사용
그러나, 무조건 코드의 재사용 때문에 상속을 하진 않음
extends
확장한다상속은
단순히 똑같은 멤버 변수와 메서드를 물려받기만 하는 것 ❌
자식 클래스에서 물려받은 멤버 변수와 메서드를 추가로 확장하는 것 ⭕
키워드 뒤에 단 하나의 클래스만 올 수 있음
→ 모호성이 발생할 수 있기 때문
👉 자바는 단일 상속(single inheritance)만을 지원함 (자세한 건 아래에서 설명)
class 자식클래스 extends 부모클래스1 {
}
class 자식클래스 extends 부모클래스1, 부모클래스2 {
부모클래스1의 변수(a);
부모클래스2의 변수(a);
}
상속은 '자식클래스 is a 부모클래스'
자식 클래스가 논리적으로 부모 클래스에 포함되어야 한다
클래스들의 관계가 일반적인(general) 개념과 구체적인(specific) 개념과의 관계를 가질 때
→ 상속하기 GOOD!
상위 클래스 : 하위 클래스보다 일반적인 개념 ex. Employee
하위 클래스 : 상위 클래스보다 구체적인 개념들이 더해짐 ex. Engineer
, Manager
. . .
상속은 클래스간의 결합도가 높은 설계
→ 상위 클래스의 수정이 많은 하위 클래스에 영향을 미칠 수 있음
👉 계층구조가 복잡하거나 hierarchy(계층의 깊이)가 높으면 좋지 않음
Student
가 Subject
를 포함하는Library
를 구현할 때 ArrayList 생성하여 사용좀 더 기능이 많고 + 구체적이야할 때
⇒ 기존 클래스를 상속 받음
같은 스타일의(결이 같은) 클래스인데, 상속 받는 클래스는 좀 더 구체적인 것
⇒ 사람 extends 포유류
상속을 활용한 클래스 구현할 때
한 클래스 안에, if~else 가 너무 많다면?
상속을 고려해봐야함
유지보수, 새로운 추가 사항 수정시 계에에속 추가 해야되는 불편함
⇒ 클래스의 단일성이 떨어짐
protected
하위 클래스에서 동일한 이름의 변수와 메서드를 재정의
class A {
String sNum;
int iNum
public void printInfo() {
System.out.println("사과는 과일이다");
}
}
class Apple extends A {
// sNum, iNum 은 상속 받음
String apple;
// 오버라이딩 // 상속 받지 않음
public void printInfo() {
System.out.println("사과는 과일이 아니다");
}
}
상위 클래스에 정의된 메서드 구현 내용 ≠ 하위 클래스에서 구현할 내용
상속을 받았다고 해서 모든 것을 그대로 쓰진 않기 때문 ⇒ 재정의 하여 구현
class A {
String aNum;
}
class Apple extends A {
String aNum; // 상속 X
}
@Override
어노테이션애노테이션 = 주석
컴파일러에게 특별한 정보를 제공해주는 역할
필요한 경우 사용자가 재정의해서 쓸 수 있음
부모 클래스에 선언한 private
멤버변수와 메소드
⇒ 자식 클래스로 상속되지 않음 ❌
메소드 오버라딩에서 부모의 접근제한보다 자식의 접근제한 범위가
확대되는 것은 가능 ⭕
축소되는 것은 안됨 ❌
접근제한 범위가 궁금하다면?
Java 시리즈의 접근 제어 지시자 페이지를 참고해주세요
calcPrice()
이름이 바뀌면 오류 뜸 @Override
public int calcPrice(int price) {
return super.calcPrice(price);
}
// 부모 클래스
public int calcPrice(int price) { // price 고객이 지불해야될 값
bonusPoint += price * bonusRatio;
return price;
}
// 자식 클래스
@Override
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;
price -= (int)(price * salesRatio); // 소수점일 수 있으니까 (int)로 처리
return price;
}
public void printInfo() {
System.out.println("사과는 과일이다");
}
public void printInfo(String fruit) { // 메서드 이름만 동일
System.out.println( + fruit + "는 과일이다");
}
int number;
public void printInfo() { // 메서드 이름 동일 + 매개변수 개수 및 타입 동일
System.out.println("사과는 과일이고, " + number + "개가 있다");
}
명령어 집합(instruction set; 인스트럭션 셋)
함수가 호출이 된다는 것 = instruction set
이 호출 된다는 것
수행하는 기능(set
)이라는 부분은?
→ instruction
마다 메모리가 따로 생기지 않음
인스턴스마다 stack
이나 heap
이 따로 생기거나, 함수가 따로 호출되는 것은?
→ 변수의 영역
👉 instruction set
과 함수가 차지한 영역은 각기 다르다
👉 instruction set
은 로직만 가지고 있다
set
프로그램이 로드되면?
→ 메서드 영역(코드 영역)에 명령어 set
이 위치
해당 메서드가 호출 되면?
→ 명령어 set
이 있는 주소를 찾아 명령어가 실행됨
다른 인스턴스라도 같은 메서드의 코드는 같음
→ 같은 메서드가 호출됨
인스턴스가 생성되면 변수는 힙 메모리에 따로 생성되지만,
메서드 명령어 set
은 처음 한번만 로드 됨
public class TestMethod {
int num;
void aaa() {
System.out.println("aaa() 호출");
}
public static void main(String[] args) {
TestMethod a1 = new TestMethod();
a1.aaa();
TestMethod a2 = new TestMethod();
a2.aaa();
}
}
스택 메모리
→ main
함수가 차지하는 영역은 코드 영역이 아니라 ‘지역변수’가 차지함
힙 메모리
→ 실제 인스턴스의 메모리
메서드 영역
→ 출력을 위해 실행되는 메소드 영역
👉 이때 만약 변수가 필요하다면? 다시 스택 메모리에 변수가 잡힘
가상 메서드 테이블(vitual method table)에서 해당 메서드에 대한 address를 가지고 있음
재정의된 경우는 재정의 된 메서드의 주소를 가리킴
함수 이름으로 호출되는 것이 아니라, 함수 이름이 가리키는 주소에 대한 table을 가리킴
가상 메서드 테이블
이 개념은 나중에 복습하면서 다시 살펴봐야겠다...