객체지향 프로그램에서 상속
: 자식이 부모를 선택해 물려받는다
: 프로그램에서는 부모 클래스를 상위 클래스라고 부르기도 하고, 자식 클래스를 하위 클래스 또는 파생 클래스라고 부른다.
상속대상
: 부모의 필드와 메소드
자식(하위) 클래스 선언시 어떤 부모(상위) 클래스를 상속받을 것인지 결정한다.
선언시 extends+부모 클래스명으로 작성해준다
class 자식클래스 extends 부모클래스 {
// 필드
// 생성자
// 메소드
}
ex)
class Child extends Parent{
// 클래스 Child는 상속받는다 Parent를
-Child 클래스를 보면 필드값을 지정 안해줬음에도 age와 name을 사용할 수 있으며, Parent 를 new 해주지 않았음에도 불러올 수 있다.
Child클래스가 Parent를 상속 받았기 때문이다.
class Parent{
// 필드
String name;
int age;
// 메소드
void cal() { }
// 생성자
// 기본 생성자
Parent(){ }
//명시적 생성자
Parent(String name, int age){
this.name = name;
this.age = age;
}
}
//클래스 child는 parent를 상속받는다
class Child extends Parent{
void childMethod() {
// 부모의 필드와 메소드 사용
// 변수를 생성해주지 않았음에도 오류없이 사용할 수 있음
age = 20;
name = "홍길동";
cal();
// 객체 생성
new Parent();
new Parent("김자바", 20);
// 그냥 일반 객체 생성도 가능
new Child1();
}
}
-상속 관계라해도 부모 클래스의 모든 필드와 메소드들을 물려받는 것은 아니다. 부모 클래스가 필드와 메소드에 private 접근 제한을 걸었다면, 그건 상속 대상에서 제외된다
-부모 클래스를 여기저기서 상속 받는 것은 상관없지만,
하나의 부모 클래스를 받아온 자식 클래스는 또 다른 부모 클래스를 만들 수 없다.
-상속받는 것은 단일 상속
// 가능한 모습
class Child extends A { }
class Child1 extends B{ }
// 여러 클래스를 부모 클래스로 가져오는 건 불가능 합니다.
class Child2 extends A extends B{ }
class Child3 extends A, B{ }
-각자 다른 클래스로 처리된다
-서로 자료를 공유한다던가 그러지 않음
-각 클래스에서 공통인 부분을 부모 클래스에 만들어놓고 사용
: 이미 잘 개발된 클래스를 재사용해 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다
-> 공통된 부분을 상위(부모)클래스로 만들고 각자 필요한 부분만 자식(하위)클래스에서 작성해 부모를 상속받으면 되는 것
1) 부모 클래스 작성
class P {
String name;
String tel;
// 명시적 생성자
// P p = new P("부모 클래스", "000"); 을 처리하기 위해 만들어줌
public P(String name, String tel) {
this.name = name;
this.tel = tel;
}
// 기본 생성자
public P() {
System.out.println("이곳은 부모 클래스 부분");
}
// Getter Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTel() { return tel; }
public void setTel(String tel) { this.tel = tel; }
}
2) 상속을 받는 부분
공통적인 이름과 번호 부분을 부모에 만들어 줬으니
각 클래스에서 필요한 국어, 영어, 토익, 역사 점수만 각자 작성해준다.
class A extends P {
int kor;
void a() {
name = "김자바";
tel = "123";
kor = 80;
}
public int getKor() {return kor;}
public void setKor(int kor) {this.kor = kor;}
//A클래스의 기본 생성자
A(){
System.out.println("이곳은 자식 클래스 A의 기본 생성자 부분");
}
}
class B extends P {
int eng;
void b() {
name = "이자바";
tel = "234";
eng = 80;
}
public int getEng() {return eng;}
public void setEng(int eng) {this.eng = eng;}
}
class C extends P {
int toeic;
void c() {
name = "정자바";
tel = "345";
toeic = 800;
}
public int getToeic() {return toeic;}
public void setToeic(int toeic) {this.toeic = toeic;}
}
class D extends P {
int history;
void d() {
name = "최자바";
tel = "456";
history = 95;
}
public int getHistory() {return history;}
public void setHistory(int history) {this.history = history;}
}
3) 출력하는 메인 작성하기
public class InherianceEx1{
public static void main(String[] args) {
// 부품 설계 후 조립하기
A a = new A();
a.a();
System.out.println(a.getName());
System.out.println(a.getTel());
System.out.println(a.getKor());
System.out.println("---------------------");
B b = new B();
b.b();
System.out.println(b.getName());
System.out.println(b.getTel());
System.out.println(b.getEng());
System.out.println("---------------------");
C c = new C();
c.c();
System.out.println(c.getName());
System.out.println(c.getTel());
System.out.println(c.getToeic());
System.out.println("---------------------");
D d = new D();
d.d();
System.out.println(d.getName());
System.out.println(d.getTel());
System.out.println(d.getHistory());
System.out.println("---------------------");
// 부모 클래스 객체 생성하기
P p = new P("부모 클래스", "000");
System.out.println(p.name);
System.out.println(p.tel);
// System.out.println(p.getKor); //부모 클래스는 자식 클래스를 사용할 수 없음, 호출할 수 없음
}
}
// 부모 클래스
class Parent{ }
// 자식 클래스
class Child extends Parent{ }
// Parent의 자식 클래스인 Child의 자식 클래스
class Child2 extends Child2{ }
-자식 클래스만이 부모 클래스의 것을 가져온다
class Parent1 {
String name;
int age;
Parent1() {
}
/*
불가능한 부분
void cal() {
dept="작곡과";
}
*/
}
class Child3 extends Parent1 {
String dept = "작곡가";
}
자식 객체를 생성하면, 부모 객체가 먼저 메모리에 올라가고 (= 생성되고) 자식 객체는 그 다음에 생성된다.
// class Child extends Parent
// 객체 생성
Child ch = new Child();
이 경우 Child 클래스만 객체 생성 한 것 같지만 메모리를 확인해보면
상위 클래스인 Parent가 먼저 올라간뒤
하위 클래스인 Child가 올라가는 모습을 확인할 수 있다
/*
자식 클래스를 객체 생성하면
부모의 기본생성자를 자동호출 하고 (메모리에 먼저 올리고)
그 다음 자식 객체의 생성자를 호출한다
몇 개를 연결해도 그 순서는 같다
가장 위에 있는 부모부터 순서대로 자동호출 한다.
자식 클래스가 객체 생성될 때는 부모 클래스의 기본 생성자로 객체화 한다
즉 부모 클래스에 기본 생성자가 없으면 에러남
*/
public class InherianceEx2 {
public static void main(String[] args) {
Bb bb = new Bb();
}
}
class Per {
String name;
String tel;
// 기본 생성자
public Per() {
System.out.println("이곳은 부모 클래스 부분");
}
}
class Aa extends Per {
// A클래스의 기본 생성자
Aa() {
System.out.println("이곳은 P의 자식 클래스 A의 기본 생성자 부분");
}
}
class Bb extends Aa {
// B클래스의 기본 생성자
Bb() {
System.out.println("이곳은 A의 자식 클래스 B의 기본 생성자 부분");
}
}
실행 결과
우리는 프로그램 실행순서가 아래와
static{} -> 인스턴스{} -> 생성자{}
그럼 상속이 있을때는?
부모 클래스의 static -> 자식 클래스의 static -> 부모클래스의 인스턴스
-> 부모클래스의기본생성자 -> 자식 클래스의 인스턴스 -> 자식 클래스의 기본생성자
// 자식 클래스에서 부모 클래스의 static을 사용할 수 있는가
// 네
1) 상속 관계를 볼 수 있는 클래스 설정
class PP{
// 먼저 메모리에 올라가는 정적 변수
static int a;
// 인스턴스 변수
int aa;
static {
a=1;
// b 사용 불가능
// System.out.println("static 블록 " +a +b);
// Ab.b 가능
System.out.println("static 블록 " +a + " "+ Ab.b);
// 불가능. 변수 올라가기 전에 static블록이 먼저 올라가서
// System.out.println("static 블록 " +a + aa);
}
{
a=3;
// 불가능
// 부모 클래스에서 자식 클래스의 인스턴스 변수를 사용할 수 없습니다
// System.out.println("인스턴스 블록~ " +a + b);
// 가능
// 변수 값을 static으로 지정해줬기 때문에, 메모리에 먼저 올라간다
// 그런 값들은 자식 클래스의 것이라도 부모 클래스에서도 불러 사용 가능
System.out.println("인스턴스 블록~ " +a + " "+ Ab.b);
// 가능
System.out.println("인스턴스 블록~ " +a + " "+ aa);
}
PP() {
a=4;
System.out.println("부모 클래스의 기본 생성자 " +a);
}
}
class Ab extends PP{
static int b;
int bb;
static {
b=100;
a=2;
//부모의 변수 사용 가능
System.out.println("이곳은 자식클래스의 static "+b);
}
{
b=200;
a=5;
System.out.println("이곳은 자식 클래스의 인스턴스~ " +b);
}
Ab() {
b=300;
a=6;
System.out.println("이곳은 자식클래스의 기본 생성자 " +b);
}
}
2) 확인할 출력(메인)클래스 작성
public class Inheritance3 {
public static void main(String[] args) {
// 부모 클래스 객체 생성
//PP p = new PP();
//자식 클래스 객체 생성
Ab aa = new Ab();
}
}
실행결과
상속을 사용하는 관계에서도 this로 다른 메소드 참조 가능
1) 부모 클래스(PPP)와 자식 클래스(Abb)
class PPP{
static int a; // 먼저 메모리에 올라가는 정적 변수
int aa; // 인스턴스 변수
}
class Abb extends PPP{
static int b;
int bb;
Abb(){
this(1000);
System.out.println("자식 클래스의 기본 생성자 부분 " + b + " " + bb);
}
Abb(int b){
// 생성자에서 또 다른 생성자 호출
this(100, 200);
System.out.println("자식 클래스의 명시적 생성자 부분 " + b + " " + bb);
}
Abb(int b, int bb) {
this.b = b;
this.b = bb;
System.out.println("자식클래스의 또 다른 명시적 생성자 부분 "
+ b + " " + bb);
}
}
2) 출력하기 위한(불러올) 메인 클래스
public class Inheritance4 {
public static void main(String[] args) {
// 객체 생성
Abb aa = new Abb();
}
}
/*
코드중
this(1000) 실행시 Abb(int b) 메소드를 불러와 사용하는 것
this(100, 200) 실행시 Abb(int b, int bb) 메소드 불러와 사용하는 것
으로 확인할 수 있다
*/
실행 결과