다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
→ 부모 클래스가 가지고 있는 필드/메서드를 자식 클래스가 물려 받아 자신의 것처럼 사용
√ 상속의 목적
클래스의 재사용, 연관된 클래스들에 대한 공통적인 규약 정의
√ 상속의 장점
1. 보다 적은 양의 코드로 새로운 클래스 작성 가능
2. 코드를 공통적으로 관리하기 때문에 코드의 추가 및 변경 용이
3. 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여
√ 방법
클래스 간의 상속 시에는 extends(확장하다) 키워드 사용
√ 표현식
[접근제한자] class 클래스명 extends 클래스명 {}
public class Academy extends Company {}
√ 단일 상속(Single Inheritance)
클래스간의 관계가 다중 상속보다 명확하고 신뢰성 있는 코드 작성
자바에서는 다중 상속 미지원 → 단일상속만 지원
√ 다중 상속(Multiple Inheritance)
C++에서 가능한 기능으로 여러 클래스로부터 상속을 받으며 복합적인 기능을 가진 클래스를 쉽게 작성 가능
서로 다른 클래스로부터 상속 받은 멤버 간의 이름이 같은 경우 문제 발생
자바는 클래스간의 다중 상속 불가
√ super()
부모 객체의 생성자를 호출하는 메서드로 기본적으로 후손 생성자에 부모 생성자 포함
후손 객체 생성 시에는 부모부터 생성이 되기 때문에 후손 클래스 생성자 안에는 부모 생성자를 호출하는 super()가 첫 줄에 존재(부모 생성자가 가장 먼저 실행되어야 하기 때문에 명시적으로 작성 시에도 반드시 첫 줄에만 작성)
매개변수 있는 부모 생성자 호출은 super(매개변수, 매개변수)를 넣으면 됨
√ super.
상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수
자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용
super : 상위(부모)
sub : 하위(자식)
자식 클래스가 상속 받은 부모 메소드를 재작성 하는 것
부모가 제공하는 기능을 후손이 일부 고쳐 사용하겠다는 의미로 자식 객체를 통한 실행 시 후손 것이 우선권을 가짐
√ 특징
//, /**/ : 사람을 위한 주석
@ (Annotation) : 컴퓨터를 위한 주석
메서드 헤드라인 위에 반드시 Annotation(== comment==주석), @Override 표시
접근 제어자를 부모 것보다 같거나 넓은 범위로 변경 가능
부모 메서드의 예외처리 클래스 처리범위보다 좁은 범위로 예외처리 클래스 수정 가능
√ 성립 조건
부모 클래스의 메서드와 자식 클래스의 메서드 비교
한 클래스 내에서 같은 이름의 메서드를 여러 개 정의하는 것
√ 성립 조건
같은 메서드 이름
다른 매개변수 선언부(매개변수 타입, 개수, 순서)
√ 주의 사항
메서드의 리턴타입은 오버로딩 조건과 관계 없음
√ final 클래스
상속이 불가능한 클래스
public final class finalClass {}
√ final 메서드
상속 시 오버라이딩이 불가능한 메서드
public final void method() {}
√ 유의 사항
클래스에 abstract와 final 동시에 사용 불가능
메서드에 static과 abstract 동시에 사용 불가능
abstract 메서드의 접근제어자로 private 불가능
package edu.kh.inheritance.dto;
// final class : 상속이 불가능한 클래스
// 보통 코드 재사용을 원치 않을 때 사용
public /*final*/ class Parent /*extends Object*/{
// 컴파일러가 자동 추가
private int money = 400_000_000;
private String lastName = "박"; // 성
// 기본 생성자
public Parent() {
System.out.println("Parent() 기본 생성자");
}
// 매개변수 생성자
public Parent(int money, String lastName) {
this.money = money;
this.lastName = lastName;
System.out.println("Parent(int, String) 매개변수 생성자");
}
// getter / setter
// final 메서드 : 오버라이딩 불가
public /*final*/ int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String toString() {
return money + " / " + lastName;
}
}
package edu.kh.inheritance.dto;
// 확장하다 (Parent의 코드를 상속받아 Child1 확장)
public class Child1 extends Parent{
private String car;
// 생성자
public Child1() {
super(); // super() 생성자
System.out.println("Child1() 기본 생성자");
}
// 매개변수 생성자
public Child1(String car) {
// super(); // super() 생성자
// → 자식 객체 생성 시 부모 객체를 먼저 생성하게 함
// ** 미작성 시 컴파일러가 자동 추가 **
// 부모로부터 상속받은 setter를 이용해서 부모필드 초기화
// setMoney(100_000_000);
// setLastName("김");
// 부모의 필드를 초기화 하는 방법이 부모 매개변수 생성자에 존재
// → 이를 호출해서 사용 (코드길이 감소, 재사용성 증가)
super(200_000_000, "백");
this.car = car;
System.out.println("Child1(String) 매개변수 생성자");
}
// getter / setter
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public String toString() {
// Parent p1 = new Parent();
// p1.getMoney();
// 자신의 메서드(같은 클래스) 호출 시 이름만 부르면 된다!
// + 상속 특징(부모 필드/메서드를 상속 받아 자식 것처럼 사용)
// System.out.println(getCar());
// System.out.println(getMoney());
// System.out.println(getLastName());
// 부모의 필드 값을 간접 접근 방법으로 얻어와 하나의 문자열로 만들어 반환
// return car + " / " + getMoney() + " / " + getLastName();
// 문제점 : StackOverflowError 발생
// 원인 : Child1의 toString() 호출 시
// 같은 toString()은 계속 반복해서 호출(== 재귀 호출)
// return car + " / " + toString();
// 해결 방법 : 부모의 toString() 호출을 명시 → super. 참조변수 이용
return car + " / " + super.toString();
}
}
package edu.kh.inheritance.dto;
public class Child2 extends Parent{
private String house;
public Child2() {
System.out.println("Child2() 기본 생성자");
}
public Child2(String house) {
this.house = house;
System.out.println("Child2(String) 매개변수 생성자");
}
public String getHouse() {
return house;
}
public void setHouse(String house) {
this.house = house;
}
// 부모의 toString() 존재
/*
public int getMoney() {
return money;
}
*/
// 자식이 상속 받은 getMoney()을 다시 작성(재정의)
// @Override : 컴파일러에게 해당 메서드는 재정의 되었다는 것을
// 알려주는 컴퓨터용 주석
// → 오버라이딩 형식이 맞는지 검사 진행
@Override
public int getMoney() {
System.out.println("오버라이딩 한 getMoney()");
return super.getMoney() + 500;
// 부모의 getMoney() 반환값에 500원 추가
}
}
package edu.kh.inheritance.run;
import edu.kh.inheritance.dto.Child1;
import edu.kh.inheritance.dto.Child2;
import edu.kh.inheritance.dto.Parent;
public class TestRun {
public static void main(String[] args) {
// 부모, 자식1,2 객체 생성
Parent p = new Parent(1_000_000_000, "김");
Child1 c1 = new Child1("포르쉐");
Child2 c2 = new Child2("시그니엘");
// 상속(extends)
// - 부모 클래스가 가지고 있는 필드, 메서드를
// 자식 클래스가 자신의 것처럼 사용 가능하게 하는 기술
System.out.println(c1.getMoney());
System.out.println(c1.getLastName());
// 메서드 오버라이딩 시 자식이 우선 순위를 가지게 된다!+
System.out.println(c2.getMoney());
System.out.println(c2.getLastName());
// → 부모의 메서드 상속 확인
// System.out.println(c1.money);
// → 부모의 private 접근 제한자는 상속을 받아도 직접 접근 불가능
// → private 필드/메서드는 상속이 되어 있음
// 단, 자식도 접근만 못할 뿐이다 (private는 같은 클래스만 접근 가능)
// 부모 클래스의 코드를 수정하면
// 자식 모두에게 적용됨(공통적인 규약, 유지보수성 향상)
System.out.println("----------------------------");
// toString : 객체가 가지고 있는 필드를 하나의 문자열로 만들어서 반환
System.out.println(c1.toString());
}
}