JAVA

[JAVA] 상속

shb 2022. 2. 9. 17:46

 상속 (Inheritance)
  상위클래스를 상속받아서 하위클래스를 정의하는 방법
    class 하위클래스 extends 상위클래스
     
  자바 에선 오로지 '하나의 부모'로부터 상속받을수 있습니다 (단일 상속)  다중 상속 허용하지 않음
   
  (용어)
  Super Class(상위 클래스), Parent Class(부모/조상 클래스), Basic Class(기본 클래스)
  Sub Class(하위 클래스), Child Class(자식 클래스), Derived Class(유도 클래스)
   ※ 상속받는다..(동사) inherit , subclass
   
  sub class에서는 super class가 가지고 있는 멤버 변수들은  선언하지 않아도 사용할 수 있다.
  super class에 없는 멤버 변수만 선언해 주면 됨
   
  상속의 이점 :
  상속을 통하여 기존의 객체를 그대로 활용하면서, 새로운 객체에서
  추가, 변경되는 부분만 작성함으로 소프트웨어 개발 효율을 높일수 있다.

 

package com.lec.java.inherit02;

public class BasicTV {
	// 멤버 변수
	boolean isPowerOn;
	int channel;
	int volume;
	
	// 메소드
	public void displayInfo() {
		System.out.println("--- TV 현재 상태 ---");
		System.out.println("전원: " + isPowerOn);
		System.out.println("채널: " + channel);
		System.out.println("볼륨: " + volume);
	} // end displayInfo()
	
} // class BasicTV
package com.lec.java.inherit02;

// BasicTV
//  └─ SmartTV
public class SmartTV extends BasicTV{ 
	
	// 새로이 추가할 멤버
	String ip;
	
	public void displayInfo() {
		super.displayInfo();  // 부모(super) 의 displayInfo() 를 먼저 실행하고
		System.out.println("IP 주소: " + ip);  // 추가되는 실행 코드
	}
}
public class Inherit02Main {

	public static void main(String[] args) {
		System.out.println("상속 (Inheritance)");

		// BasicTV 클래스의 인스턴스 생성
		BasicTV tv1 = new BasicTV();
		tv1.displayInfo();
		
		System.out.println();

		// SmartTV 클래스의 인스턴스 생성
		SmartTV tv2 = new SmartTV();
		tv2.isPowerOn = true;
		tv2.channel = 100;
		tv2.volume = 10;
		tv2.ip = "192.168.0.110";
		tv2.displayInfo();
		

		System.out.println("\n프로그램 종료");
	} // end main()
} // end class

 

*  자바의 모든 클래스는 java.lang.Object로부터 상속 받는다.
 *  java.lang.Object 클래스는 모든 클래스의 부모클래스이다.
 *  Object 클래스에 있는 메소드를 다른 클래스에서도 사용 가능

 

package com.lec.java.inherit03;

public class Person {
	String name;
	
	public void whoAmI() {
		System.out.println("제 이름은 " + name + " 입니다.");
	}
}
package com.lec.java.inherit03;

public class BusinessPerson extends Person {
	String company;
	
	public void showInfo() {
		whoAmI();
		System.out.println("회사는 " + company + " 입니다");
	}
}
public class Inherit03Main {

	public static void main(String[] args) {
		System.out.println("상속 연습");
		System.out.println("java.lang.Object");
		
		Person p1 = new Person();
		p1.name = "홍길동";
		p1.whoAmI();
		
		System.out.println();
		
		BusinessPerson p2 = new BusinessPerson();
		p2.name = "허균";
		p2.whoAmI();
		
		
		// toString() 은 Object의 메소드
		
		System.out.println(p2);
		System.out.println(p2.toString());  
		
		p2.company = "(주)재택";
		p2.showInfo();
		
		
		System.out.println("\n프로그램 종료");
	} // end main()
} // end class

 

상속에서 생성자 호출순서
    1. 자식 클래스의 생성자에서 명시적으로 부모 클래스의 생성자가 호출되지 않으면,

       자동으로 부모 클래스의 "디폴트 생성자"가 호출됨.
     
    2. 자식 클래스의 생성자에서 명시적으로 부모 클래스의 생성자를 호출하기도 함
     1) super(...) 키워드 사용 -> 부모 클래스의 생성자를 호출
     2) (주의) super는 항상 제일 처음에 호출되어야 함
     3) 부모 클래스에 디폴트 생성자가 없는 경우도 있을 수 있다.
    그런 경우에는 다른 생성자를 "반드시 명시적으로 호출"해 줘야만 함.


  어떤 경우에 상속으로 객체를 설계하나?
   HAS-A 관계 ===>  멤버로 설계
   Car, Tire
   Car is-a Tire  (X)
   Tire is-a Car (X)
   Car has-a Tire (OK)
 
    IS-A 관계 ===>  상속으로 설계  
    Vehicle is-a Car  (NO)  
    Car is-a Vehicle  (OK)
    HybricCar is-a Car (OK) 

 

package com.lec.java.inherit04;

public class Vehicle {
	
	int speed;
	
	// 생성자
	public Vehicle() {
		System.out.println("Vehicle() 생성");
	}
	
	public Vehicle(int speed) {
		this.speed = speed;
		System.out.println("Vehicle(int) 생성: speed=" + speed);
	}
}
package com.lec.java.inherit04;

public class Car extends Vehicle {

	int oil;
	
	// 생성자
	public Car() {
		// 부모클래스의 기본생성자 호출 --> Vehicle()
		// 명시적으로 super() 가 없으면 기본적으로 부오의 기본생성자 호출 		
		System.out.println("Car() 생성");
	}
	
	public Car(int oil) {
		// 명시적으로 부모 생성자 호출
		super();  // super 는 반.드.시 첫번째 문장이어야 한다.
		System.out.println("Car(int) 생성: oil=" + oil);
		this.oil = oil;
	}
	
	public Car(int speed, int oil) {
		super(speed);  // Vehicle(int) 호출
		this.oil = oil;
		System.out.println("Car(int,int) 생성: speed=" + speed 
				+ "oil=" + oil);		
	}
}
package com.lec.java.inherit04;

public class HybridCar extends Car {

	int electricity;
	
	// 생성자
	public HybridCar() {
		System.out.println("HybridCar() 생성");
	}
	
}
public class Inherit04Main {

	public static void main(String[] args) {
		System.out.println("상속과 생성자");
		
		System.out.println();
		// Vehicle 클래스의 인스턴스 생성
		Vehicle v1 = new Vehicle();
		
		System.out.println();
		// Car 클래스의 인스턴스 생성
		Car car1 = new Car();
		
		
		
		System.out.println();
		// HybridCar 클래스의 인스턴스 생성
		HybridCar car2 = new HybridCar();
		
		
		System.out.println();
		Car car3 = new Car(450);
		
		System.out.println();
		Car car4 = new Car(100, 6);

		System.out.println("\n프로그램 종료");
	} // end main()
} // end class

 

 상속과 접근권한
  private: 자기 클래스에서만 사용 가능
  default: 자기 글래스 + 같은 패키지 안에서 사용 가능
  protected: 자기 글래스 + 같은 패키지 + '상속 클래스'에서 사용 가능 ★
  public: 어디서나 직접 사용 가능

 

package com.lec.java.inherit05;

//private: 자기 클래스에서만
//default: 자기 클래스 + 같은 패키지 안에서
//protected: 자기 클래스 + 같은 패키지안 + 상속 관계에 있을 때
//public: 어디서나
//직접 사용 가능

public class Test01 {
	private int privateNum;
	int defaultNum;
	protected int protectedNum;
	public int publicNum;
	
	public void showInfo() {
		System.out.println("private = " + privateNum);
		System.out.println("default = " + defaultNum);
		System.out.println("protected = " + protectedNum);
		System.out.println("public = " + publicNum);
	}
	
} // end class Test01

 

메소드 재정의(Overriding)
  '상속'관계에서 부모 클래스에 있는 메소드를 자식 클래스에서 

  리턴 타입, 매개변수 모두 동일하게 유지하면서 메소드의 본체를 다시 정의하는 것
  부모 클래스에 있는 메소드와 매개변수 리스트가 동일해야 함
  부모 클래스에 있는 메소드와 접근권한 수식어가 동일할 필요는 없지만, 접근권한의 범위가 축소될 수는 없다.
  즉, 접근권한은 같거나 더 넓은 수식어를 사용해야 함.

 
    ! 메소드 오버로딩(Overloading)과 혼돈하지 말자!

메소드 중복정의(Overloading)
 1. 매개변수의 타입이 다르거나
 2. 매개변수의 개수가 다를 때
 3. 매개변수의 순서를 달리하여
 같은 이름으로 (다른 기능을 하는) 메소드를 중복 정의하는 것
      
 final 메소드 : 더이상 오버라이딩 불가
 final 클래스 : 더이상 상속 불가 

 

package com.lec.java.inherit07;

public class Person {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	protected void showInfo() {
		System.out.println("이름: " + name);
	}
	
	// final 메소드는 더이상 오버라이딩 불가
	public final void whoAreYou() {
		System.out.println("이름: " + name);
	}
}
package com.lec.java.inherit07;

public class BusinessPerson extends Person {
	
	private String company;

	public String getCompany() {
		return company;
	}

	public void setCompany(String company) {
		this.company = company;
	}
	
	@Override
	protected void showInfo() {
//	public void showInfo() {
//	private void showInfo() {  // 에러. 접근권한범위가 더 좁아질수는 없다. 
		super.showInfo();  // 부모(super)의 showInfo() 호출
		System.out.println("회사: " + company);
		
	}

	// 메소드 중복정의(Overloading)
	// 1. 매개변수의 타입이 다르거나
	// 2. 매개변수의 개수가 다를 때
	// 3. 매개변수의 순서를 달리하여
	// 같은 이름으로 (다른 기능을 하는) 메소드를 중복 정의하는 것
	public void showInfo(int id){
		System.out.println("id: " + id);
		System.out.println("이름: " + getName());
		System.out.println("회사: " + company);
	}
	
	// Object 의 메소드들도 오버라이딩 가능
	@Override
	public String toString() {
		return "BusinessPerson:" + getName() + " " + getCompany();
	}
	
	// 이클립스에서 
	// ALT + SHIFT + S, V 를 누르면 오버라이드 진행
	//  ※ ALT + SHIFT + S 를 Show Source Quick Menu 라 함
	
//	@Override
//	public void whoAreYou() {
//		
//	}

클래스 접근권한 수식어:
  1. public class: 어디서나 상속이 가능한 클래스
  2. (default) class: 같은 패키지 안에서만 상속이 가능한 클래스
  3. final class: 상속될 수 없는 클래스

 

public 클래스인 TestPublic은 다른 패키지에 있는 클래스(Test1)에서도 상속될 수 있다.

default 클래스는 다른 패키지의 클래스에서는 보이지 않는다.
다른 패키지의 클래스에서는 상속될 수 없다.