티스토리 뷰

개인공부/백기선 선장 JAVA Study

class - 6 (상속)

날따라해봐요요롷게 2021. 5. 3. 20:26

학습할 것 (필수)

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

상속이란

- 부모 클래스에 있는 멤버변수 메소드를 자식 클래스가 물려받아 속성이나 기능을 확장하여 클래스를 구현

- 부모 클래스의 기능들을 보다 더 구체적으로 구현하기 위해서 사용.

 

상속하는 클래스 : 부모클래스, parent class, base class, super class

상속받는 클래스 : 자식클래스, child class, derived class, sub class

 

상속문법

A : super class, B : sub class

 

B class extends A {

}

 

상속의 특징

- 상속할 수 있는 상위클래스는 1개이다. -> 단일 상속만을 한다.

- 상위클래스 생성자는 상속되지 않는다.

- 하위 클래스는 상위 클래스의 멤버변수와 메소드를 모두 상속받는다.

- 상위 클래스의 메소드와 멤버변수가 private 접근 제어자를 사용하는 경우

  멤버변수는 바로 접근 불가능, 메소드 상속되지 않는다.

- 상위 클래스와 하위 클래스의 멤버 변수의 이름이 같을 경우 하위 클래스의 멤버 변수가 상위 클래스의 변수를 덮는다.

- 최상위 클래스는 Object 클래스이고, 모든 클래스는 Object 클래스의 하위 클래스이다.

 

상속의 생성자

상위 클래스를 상속받은 하위 클래스를 생성할 경우 생성자는 어떻게 만들어지는가?

class Ex1 {
	int num1, num2;
	String name;
	
	public Ex1() {
		System.out.println("Super Class constructor!!!");
	}
}
public class Ex extends Ex1 {
	
	public Ex() {
		System.out.println("Sub Class constructor!!!");
	}

	public static void main(String[] args) {
		Ex ex = new Ex();
	}
}

///////// 출력
Super Class constructor!!!
Sub Class constructor!!!

하위 클래스의 객체를 생성할 경우 자연스럽게 상위 클래스의 생성자를 먼저 생성하고 하위 클래스의 생성자를 생성하는걸 볼 수 있다.

하위 클래스를 생성하면 상위 클래스의 생성자가 호출 되는 이유는 아래와 같다. (super 키워드)

super 키워드

- 하위 클래스에서 가지는 상위 클래스에 대한 참조 값. (상위 클래스의 주소를 갖고 있는 값)

- 상위 클래스의 기본 생성자를 호출한다.

- 매개 변수가 있는 상위 클래스의 생성자인 경우 하위클래스 생성자에서는 super()를 이용하여 매개변수에 맞는 생성자를 명시하여 호출한다.

- super는 상위 클래스의 인스턴스 값을 참조하는 참조 값으로 상위 클래스의 멤버변수, 메서드를 super()를 통해 접근 가능하다.

 

class Ex1 {
	int age, height;
	String name;
	
	public Ex1(int num1, String name) { // 인수가 있는 생성자!
		System.out.println("Super Class constructor!!!");
		this.age = age;
		this.name = name;
	}
}
public class Ex extends Ex1 {
	
	String weight;
	
	public Ex(int num1, String name, String weight) {
		super(num1, name); // 디폴트 생성자가 아닌 경우 상위 클래스의 생성자를 조건에 맞게 생성
		this.weight = weight;
		System.out.println("Sub Class constructor!!!");
	}

	public static void main(String[] args) {
		Ex ex = new Ex(100, "짱구", "100kg");
	}
}

이 전 코드와 다른 위의 코드를 살펴보자.

- 상위 클래스의 생성자 => 인수가 있는 생성자(디폴트 생성자 X)

- 하위 클래스의 생성자 => 상위 클래스의 생성자가 디폴트 생성자가 아니다. 그렇다면 상위 클래스의 생성자의 조건에 맞게 super() 키워드를 사용하여 인수에 맞게 생성자를 구현한 코드를 작성해야 한다. 그렇지 않으면 오류 발생.

 

이전 코드에서 생성자를 따로 구현하지 않고 디폴드 생성자를 호출할 경우 하위 클래스가 생성될 때 해당 생성자에서 컴파일 과정 전에 super() 키워드를 생성하고 컴파일한다. 즉, super() => 디폴트 생성자를 생성한다.

 

형 변환

형 변환은 자바의 특징인 다형성 중에 하나이다.

아래의 코드를 살펴보자.

class Ex1 {
	public String x() {return "EX1.x";}
}
class Ex2 extends Ex1 {
	public String x() {	return "EX2.x";}
	public String y() { return "EX2.Y"; }
}
public class Ex {
	public static void main(String[] args) {
		Ex1 ex = new Ex2();
		System.out.println(ex.x());
		//ex.y(); ===> ERROR!!
	}
}
//////////  출력
EX2.x

 

- Ex2는 Ex1을 상속한다.

- 객체 생성을 살펴보면 자료형은 Ex1의 클래스를 갖고 생성한 인스턴스는 Ex2의 클래스이다. => ex 객체는 Ex1의 형태를 갖는것 처럼 행새를 하는 것이다.

- 코드를 살펴보면 ex객체는 Ex1의 자료형이기에 y()메소드를 호출하려 하면 호출 되지 않는다. ==> 에러 발생!

- 그에 반해 ex객체가 x() 메소드를 호출하면 Ex2의 메소드가 호출된다. ==> Ex1을 상속한 Ex2는 x()메소드를 오버라이딩 하였다. 오버라이딩 된 메소드는 Ex2의 인스턴스로 생성된 ex가 호출할 수 있다.

(형변환 관련 설명은 이해가 어려워서 어수선하다....)

 

다형성을 사용하는 이유를 예를 통해서 살펴보자.

abstract class Calculator{
	int num1, num2;
	
	public void setOperands(int num1, int num2) {
		this.num1 = num1;
		this.num2 = num2;
	}
	
	int _sum() {
		return num1+num2;
	}
	
	public abstract void sum();
	public abstract void avg();
	
	public void run() {
		sum();
		avg();
	}
}
class Plus extends Calculator{

	@Override
	public void sum() {
		System.out.println("sum : " + _sum());
	}

	@Override
	public void avg() {
		System.out.println("avg : " + (this.num1 + this.num2)/2);
	}
}
class Minus extends Calculator {

	@Override
	public void sum() {
		System.out.println("sum : " + _sum());
	}

	@Override
	public void avg() {
		System.out.println("avg : " + (this.num1+this.num2)/2);
	}
}
public class Ex {

//	public static void execute(Plus cal) {
//		System.out.println("execute메소드 ==> Calculator run()메소드 ==>  실행!!!");
//		cal.run(); // Calculator 의 run()메소드 실행
//	}
    
//   public static void execute(Minus cal) {
//		System.out.println("execute메소드 ==> Calculator run()메소드 ==>  실행!!!");
//		cal.run(); // Calculator 의 run()메소드 실행
//	}
	
	public static void execute(Calculator cal) {
		System.out.println("execute메소드 ==> Calculator run()메소드 ==>  실행!!!");
		cal.run(); // Calculator 의 run()메소드 실행
	}
	public static void main(String[] args) {
		//다형성(형변환)을 사용하지 않은 코드
//		plus exPlus = new plus();
//		exPlus.setOperands(10, 10);
//		exPlus.run();
//		minus exMinus = new minus();
//		exMinus.setOperands(-10, -10);
//		exMinus.run();
		
		/*다형성, 형변환을 사용한 코드*/
		Calculator exPlus = new Plus();
		exPlus.setOperands(10, 10);
		
		Calculator exMinus = new Minus();
		exMinus.setOperands(-10, -10);
		
		execute(exPlus);
		execute(exMinus);
	}
}

(참조 : 생활코딩-다형성)

- 추상클래스(Calculator)를 상속받은 두 클래스(Minus, Plus)는 추상메소드(sum() / avg())를 오버라이딩하여 메소드를 구현한다.

- 메인 메소드에서 각각의 객체의 자료형을 추상클래스로 하고 상속받은 두 클래스의 인스턴스로 생성하였다. (업캐스팅)

- 형변환(업캐스팅)을 인하여 execute() 메소드의 매개 값으로 추상클래스를 자료형으로하는 객체를 넘겨주어  해당 객체의 메소드를 호출할 수 있도록 구현하였다.

- 형변환을 사용하지 않을 경우 각각의 클래스 객체를 호출하는 execute()메소드를 각각 생성해야 해서 실행해야 하지만 형변환을 이용하여 객체를 넘겨주어 실행할 수 있다.

 

메소드 오버로딩 / 오버라이딩

#오버로딩 (overloading)

- 상속관계에 있는 클래스 사이에서 두 클래스에 인자의 수와 데이터 타입을 다르게 설정하여 생성한 메소드를 말한다.

- 오버로딩은 상속관계가 없기도 하다. 하나의 클래스 내에 이름이 같은 메소드를 사용하는 경우를 말하기도 하다.

class Overloading_super{
	int num1=0, num2;
	String name;
	
	public void x() {
		System.out.println("super클래스 메소드 ==> 매개변수 0개");
	}
	public void x(int num) {
		this.num1 = num;
		System.out.println("super클래스 메소드 ==> int형 매개변수 1개 : " + num1);
	}
}

public class Overloading_sub extends Overloading_super{
	
	public void x(String name) {
		System.out.println("sub클래스 메소드 ==> String 매개변수 : " + name);
	}

	public static void main(String[] args) {
		Overloading_super ex = new Overloading_super();
		Overloading_super ex1 = new Overloading_sub();
		Overloading_sub ex2 = new Overloading_sub();
		
		ex.x(5); ex.x(11);
		ex1.x(); ex1.x(10);
		ex2.x("서브클래스 객체!!"); ex2.x(); ex2.x(11);
	}
}
///////////////////////////////////////////////////////////
super클래스 메소드 ==> int형 매개변수 1개 : 5
super클래스 메소드 ==> int형 매개변수 1개 : 15
super클래스 메소드 ==> 매개변수 0개
super클래스 메소드 ==> int형 매개변수 1개 : 10
sub클래스 메소드 ==> String 매개변수 : 서브클래스 객체!!
super클래스 메소드 ==> 매개변수 0개
super클래스 메소드 ==> int형 매개변수 1개 : 11

- 상속관계의 클래스에 x()의 이름으로 생성된 메서드가 존재한다.

- 각각의 메소드들은 매개변수가 다르다. ==> sub 클래스는 super 클래스의 x() 메소드를 모두 사용할 수 있다.

 

#메소드 오버라이딩 (@override)

 : 상속관계의 클래스에서 부모클래스의 메소드와 이름이 같고, 인자의 수와 반환형이 같은 메소드를 자식클래스에서 재정의하여 사용하는 것을 오버라이딩이라고 한다.

 

특징 : 자식 클래스는 부모 클래스의 private 멤버를 제외한 모든 메소드를 상속받는다. (상속의 특징)

- 오버라이딩이란 동작(기능)만 재정의하는 것이므로, 메소드의 선언부는 기존 메소드와 같아야 한다. but. 메소드의 반환타입이 부모 클래스의 반환타입으로 "타입 변환" 할 수 있다면 변경할 수 있다.

- 부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다.

- 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다.

(참조 : www.notion.so/e5c33507880b4d098f83a2c4f8f02c04)

class Override_super{
	void x() {
		System.out.println("부모클래스 메소드!!");
	}
	
	public String y(String name) {
		System.out.println("부모클래스 메소드 ==> 반환형 :String - 인자값 : String 1개" + name);
		return "부모클래스~~";
	}
}

public class Override_sub extends Override_super {
	void x() {
		System.out.println("자식클래스 메소드");
	}
	public String y(String name) {
		System.out.println("자식클래스 메소드 ==> 나는 조금 다르지..-- " + name);
		return "자식클래스 - 오버라이딩 됨!";
	}
	//오버라이딩은 반환값과 인자수 모두 동일한 메소드를 재정의 하는 것이다. 하지만 반환값을 상속클래스 형으로 바꾸어 오버라이딩할 수 있다.
	public Override_super y() {
		System.out.println("반환형이 부모클래스형인 메소드 오버라이딩하기");
		Override_super returnValue = new Override_super();
		return returnValue;
	}
	public static void main(String[] args) {
		Override_super ex = new Override_super();
		Override_super ex1 = new Override_sub();
		Override_sub ex2 =  new Override_sub();
		ex.x(); ex.y("whiteship");
		ex1.x(); ex1.y("whiteship1"); //ex1.y(); ==> 에러
		ex2.x(); ex2.y("whiteship2");  ex2.y();
	}
}
/////////////////////////////////////////////
부모클래스 메소드!!
부모클래스 메소드 ==> 반환형 :String - 인자값 : String 1개whiteship
자식클래스 메소드
자식클래스 메소드 ==> 나는 조금 다르지..-- whiteship1
자식클래스 메소드
자식클래스 메소드 ==> 나는 조금 다르지..-- whiteship2
반환형이 부모클래스형인 메소드 오버라이딩하기

- 오버로딩과 다르게 형변환된 객체가 부모클래스의 메소드를 호출하는 것이 아닌 자식클래스의 메소드를 호출하는 것을 살펴볼 수 있다. => 부모클래스의 메소드를 덮고 자식클래스의 메소드를 호출하는 것을 => 오버라이딩!

- 형변환한 ex1.y() 가 발생하는 이유는?? ==> 

추상 클래스

- 한 개 이상의 추상 메소드를 갖는 클래스

- 일반 클래스의 메소드는 자세히 구현된 기능을 갖지만, 추상클래스의 메소드는 추상적이다.

- 추상클래스는 제대로 구현되지 않은 메소드를 갖는 불완전한 클래스로 절대! 객체로 생성되어서는 안된다.

- 추상클래스를 사용하기 위해서는 반드시 상속하여 상속한 클래스의 객체를 이용하여 메서드를 오버라이딩하여 사용한다.

- 만약 추상메서드 중 하나라도 구현하지 않는다면, 자손 클래스 역시 추상클래스로 지정해 주어야 한다.

 

#추상메소드

- 내용이 없는, 기능이 구현되지 않은 메소드.

- 추상 메서드란 메서드의 시그니처(리턴 타입, 메서드명, 매개변수)만 정의되고 구체적인 행위, 즉 블록({}) 부분은 정의되지 않은 메서드를 의미

- 사용 이유 : 메소드가 일반적인 내용인 경우에 부모클래스에서 정의하지 않고 상속받은 자식클래스에서 재정의 (오버라이딩)하여 구현하기 위해서 사용.

- 정의 : abstract 리턴타입 메소드명 (매개변수);

 

 

정의 : 클래스 앞에는 [public / final / abstract] 접근제어자가 붙을 수 있다.

abstract class 클래스명 { ~~ }

abstract class Abstract_super {
	abstract void x();
	// 추상메소드는 메소드의 시그니쳐 (리턴타입, 매개변수, 메서드명)만을 입력할 수 있다. 내용을 구현하지는 않는다.
	// 추상메소드 정의 ==> abstract 리턴타입 메소드명 (매개변수);
	abstract String y(String talk);
}

public class Abstract_Ex extends Abstract_super{
	
	@Override
	void x() {
		System.out.println("추상메소드 x() 구현!!");
	}

	@Override
	String y(String talk) {
		System.out.println("추상메소드 y() 구현!!");
		return talk;
	}

	public static void main(String[] args) {
		Abstract_Ex ex = new Abstract_Ex();
		Abstract_super ex1 = new Abstract_Ex();
		//Abstract_super ex2 = new Abstract_super(); ==> 추상클래스 객체 생성 안됨!!
		ex.x(); System.out.println(ex.y("추상메소드!! 매개변수 보내기!!"));
		ex1.x(); System.out.println(ex1.y("추상메서드 매개변수 보내기~~~"));
	}
}
////////////////////////////////// 출력
추상메소드 x() 구현!!
추상메소드 y() 구현!!
추상메소드!! 매개변수 보내기!!
추상메소드 x() 구현!!
추상메소드 y() 구현!!
추상메서드 매개변수 보내기~~~

 

 

final 키워드

- final은 자바의 Modifier(제어자) 이다.

- final은 클래스, 메소드, 변수 앞에 붙어 사용된다.

 

 

#변수 : final 변수;

 - 변수 앞에 final 이 붙으면 단 한 번의 초기화만 가능하고 이후에 변경이 불가하다. ==> 상수가 된다.

 - static 제어자의 변수인 클래스 변수인 경우 변수를 변경하는 것을 막기위해 final 키워드를 사용한다.

 - 인스턴스 변수와 구분하기 위해서 대체로 대문자로 선언하며 단어와 단어 사이에는 _(언더바)를 사용한다.

final static int METHOD_NUMBER = 3;

#메소드 : 접근제어자 final 반환형 메소드() 

- final 메소드를 사용하는 경우는 상속관계를 예를 들 수 있다. 

 : 메소드 재정의(메소드 오버라이딩)는 부모클래스의 메소드를 상속받지 않는다. 부모클래스의 메소드를 오버라이딩 하는것을 막기 위해 메소드에 final 제어자를 사용한다.

class Final_Method{
	public final void method() { System.out.println("파이널 메소드");}
}
class Final_MethodTest extends Final_Method{
	// public final void method() {System.out.println("메소드 오버라이딩!"); } ==> 재정의 안됨!!
}

#클래스 : final class 클래스명{}

- final 로 선언된 클래스는 상속될 수 없다. ==> 클래스 사용을 못하게함.

final class Final_Method{
	public final void method() { System.out.println("파이널 메소드");}
}
class Final_MethodTest /*extends Final_Method*/ 
{
	// public final void method() {System.out.println("메소드 오버라이딩!"); }
}

 

 

Object 클래스

 

 

(참조 : www.notion.so/e5c33507880b4d098f83a2c4f8f02c04)

(출처: https://all-record.tistory.com/90 [세상의 모든 기록])

출처: https://data-make.tistory.com/205 [Data Makes Our Future]

'개인공부 > 백기선 선장 JAVA Study' 카테고리의 다른 글

class - 8 (인터페이스)  (0) 2021.05.11
class - 5 (클래스)  (0) 2021.04.27
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함