티스토리 뷰
1. 상속( Inheritance )의 개념
- 자식 클래스가 부모 클래스의 멤버를 물려받는 것
- 자식이 부모 클래스를 선택해서 물려받는다.
- 상속 대상 : 부모 클래스의 필드와 메소드 ( 생성자는 물려받지 않는다. )
- 자식 클래스 : 부모 클래스의 필드와 메소드 + 자식 클래스에서 추가한 필드와 메소드
- 상속을 통해서 객체의 다향성을 구현할 수 있다.
- 상속 대상 제한 :
- 1 ) 부모 클래스의 private 접근제한자를 갖는 필드와 메소드는 제외된다.
- 2 ) 부모 클래스가 다른 패키지에 있을 경우, default 접근제한자를 갖는 필드와 메소드도 제외된다.
- + 자식 클래스일지라도 외부 클래스로 간주되기 때문에 private로 제한된다.
2. 클래스 상속 ( extends )
- Extends 키워드 : 자식 클래스가 상속할 부모 클래스를 지정하는 키워드
- Extends는 확장된다는 의미로, 자식 클래스가 부모클래스를 상속 받음으로써 확장된다는 의미이다.
- Ex. public class B extends A { } : B클래스에서 A클래스를 부모로 지정하여 상속한다.
- 자바에서는 단일 상속(단 1개의 부모)이 되기에, 부모 클래스를 나열할 수 없다.
3. jar 파일 ( Lombok 설치 )
- jar 파일이란 : 자바 언어로 만든 압축/해체 프로그램
- XXXXX.jar 파일 관리 방법 :
- (1) XXXX.jar 파일의 내용 보기
- C:\temp> jar tvf XXXX.jar
- (2) XXXX.jar 파일의 해체(압축풀기)
- C:\temp> jar xvf XXXX.jar
- (3) XXXX.jar 파일의 생성(압축생성)
- C:\temp> jar cvf XXXX.jar 파일1 파일2 디렉토리1 디렉토리2
- Lombok agent와 library jar 파일의 용도는 참조타입 여러 개 중에 class 선언할 때 사용한다.
4. 부모 생성자의 호출 ( super ( . . . ) )
- 부모 없는 자식은 없기에, 부모객체가 먼저 생성된 후에 자식객체가 생성되게 된다.
- 부모 생성자 호출 완료 후에 자식 생성자 호출이 완료된다.
- Ex. DmbCellPhone dmbCellPhone = new DmbCellPhone() ; 일 때,
- 스택 영역에 dmbCellPhone가 생성되고
- 힙 영역에는 부모객체(CellPhone)가 생성된 후에, 자식객체(DmbCellPhone)가 생성된다.
- Super은 부모생성자로 부모객체의 주소를 들고 있는 키워드이다.
- 부모생성자가 없다면 컴파일 오류가 발생할 수 있다.
- 부모 클래스에 매개변수가 있을 경우에는 필수로 “super();”를 작성해야 한다.
5. 다향성의 조건
- 1 ) 부모 / 자식 상속관계를 가져야 한다!
- 2 ) 자식 클래스에서 부모 클래스의 메소드를 재정의(메소드 오버라이딩) 해야 한다.
- 3 ) 자동형변환 조건이 성립되어야 한다.
6. 메소드 재정의 ( Over ride )
- 부모 클래스의 상속 메소드를 수정해 자식 클래스에서 재정의 하는 것이다.
- + 오버 로딩과 오버 라이딩은 서로 다른 것이기에 구분해야 한다.
- 메소드 재정의 조건 :
- 1 ) 부모 클래스의 메소드와 동일한 시그니처를 가져야 한다.
- 2 ) 부모의 접근 제한자보다 더 강하게는 오버 라이딩이 불가능 하다.
- + default를 public으로 바꾸는 것은 가능하지만, public을 default나 private로 변경하는 것을 불가능 하다. ( 주로 똑같이 사용하기는 한다. )
- 3 ) 새로운 예외(버그) throws 불가능하다.
- 메소드 재정의는 부모 클래스에서 상속받은 메소드에서만 가능하다!
- 요약 :
- 1. 부모 / 자식 관계이어야 한다!!
- 2. 부모에게서 상속받은 메소드의 껍데기는 그대로 받아서, 중괄호 { } 블록 내에서만 재정의 한다.
- @Override 어노테이션 ( 메소드 재정의 )
- : 컴파일러에게 부모 클래스의 메소드 선언부와 동일한지 검사를 지시한다.
- : 정확한 메소드 재정의를 위해 붙여주면 된다.
- 메소드 재정의 효과 : 부모의 오리지날(원래) 메소드가 숨겨지는 효과가 발생하고 재정의된 자식 메소드가 실행된다.
7. 다형성 ( polymorphism )
- 같은 부모 타입이지만, 부모타입에 대입된 자식객체가 부모타입의 선언된 메소드를 재정의한 경우 다양한 객체를 대입하는 것이 가능한 성질
- 이때 다양한 성질은 부모타입의 메소드를 수행했을 때 대입된 자식객체의 재정의된 다양한 성질을 의미하며, 이를 통해 다양한 결과가 나오는 이 성질을 “다형성”이라고 한다.
- 부모 타입에는 모든 자식 객체가 대입 가능하다.
- 자식 타입은 부모 타입으로 자동타입 변환된다.
- Ex. 자동차는 타이어 타입으로 한국 타이어와 금호 타이어를 사용하지만, 각 타이어의 성능은 다르게 나온다. 하지만 금호타이어나 한국 타이어는 타이어 타입이 정한 규격을 지켜야 한다.
- 부모타입 변수명 = new 자식타입 ( ) ;
01. 과제 8 - 펙토리얼 구하기!
package homeworks;
public class Factorial {
/**
*
* 과제 8 - factorial 구하기
*
* (1) n! -> n factorial
*
* if n = 2, 2! = 2x1
* if n = 3, 3! = 3x2x1
* if n = 7, 7! = 7x6x5x4x3x2x1
*
* (2) n!에서 n의 숫자가 몇일 때, 여러분 pc로 답이 안나오는지 알려주세요!
*
* for문 / while문 / 구글링 ( self information )
*
* 배운 것을 기반으로 완성시켜 보시오!
*
* 코드를 다양하게 작성해서 제출해 보기.
*
* 결과가 나오는데 걸리는 시간을 측정해 보시오.
*
* */
public static long Factorial ( long x ) {
long result1 = 1l; // 팩토리얼의 결과 값
for ( long i = 1l; i<= x; i++) {
result1 = result1 * i;
} // for
System.out.println (x + "! = " + result1);
return result1;
} // static
public static void main (String[] args) {
for ( long i = 1l; i <=100; i ++) {
if( Factorial(i) < 0) { // 여기에서 factorial은 리턴값만이 아니라 메소드가 불러오기에 출력이 된다.
System.out.println( i + "번째서부터 오류가 납니다!" );
break;
} // if - break
} // for
// 출력 결과 :
// 1! = 1
// 2! = 2
// 3! = 6
// 4! = 24
// 5! = 120
// 6! = 720
// 7! = 5040
// 8! = 40320
// 9! = 362880
// 10! = 3628800
// 11! = 39916800
// 12! = 479001600
// 13! = 6227020800
// 14! = 87178291200
// 15! = 1307674368000
// 16! = 20922789888000
// 17! = 355687428096000
// 18! = 6402373705728000
// 19! = 121645100408832000
// 20! = 2432902008176640000
// 21! = -4249290049419214848
// 21번째서부터 오류가 납니다!
} // main
} // end class
02. lombok과 부모자식관계
package Practice;
//@NoArgsConstructor // 어노테이션마다 위치가 지정되어있는 것이 있을 수 있으니 확인해봐야 한다.
public class CellPhone { // 전화기
// @NoArgsConstructor는 바이트 코드를 직접 조작해서 클래스의 멤버를 만들어 준다.
// lombok이 기본생성자를 생성해 준다.
// 1. 고유속성 필드
String model;
String color;
// 2. 생성자 -> 자바 컴파일러의 default constructor
public CellPhone() {
// this.model = model;
// this.color = color;
//
System.out.println("CellPhone() invoked"); // --- > 부모 객체가 자식 객체보다 먼저 생성됨을 알 수 있다.
} // default constructor를 생성하면 컴파일 오류가 발생하게 된다.
// --- > 그 이유는 lombok이 바이트 코드를 조작하여 생성자를 직접 생성하였기에 생성자가 중복되었기 때문이다.
// 위와 같이 매개변수가 있을 경우에는 super();를 활용하여 자식클래스에 생성해 줘야 한다!! (**)
// 만약 부모 클래스 생성자가 없을 경우에는 super();를 직접 입력하지 않아도, 자식클래스에서 사용이 가능하다. (***)
public CellPhone(String model) {
System.out.println("CellPhone(String model) invoked");
this.model = model;
} // overRoading 1
public CellPhone(String model, String color) {
System.out.println("CellPhone(String model, String color) invoked");
this.model = model;
this.color = color;
} // overRoading 2
// 3. 메소드
void powerOn() {System.out.println("전원을 킵니다.");}
void powerOff() {System.out.println("전원을 끕니다.");}
void bell() {System.out.println("벨이 울립니다.");}
void sendVoive(String message) {System.out.println("자신 : " + message);}
void receiveVoice(String message) {System.out.println("상대방 : " + message);}
void hangUp() {System.out.println("전화를 끊습니다.");}
} // end class
package Practice;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor // 모든 필드를 갖는 생성자를 만들어주지만, 이때 상속관계는 고려하지 않는다.
// 지금은 필드가 int 1개 분이기에 DmbCellPhone(int)가 생성된다.
public class DmbCellPhone extends CellPhone { // 부모자식 관계 (****)
// 1. 필드
int channel;
// 2. 매개변수가 있는 생성자
DmbCellPhone (String model, String color, int channel) {
// super()로 부모객체의 생성자를 출력해 보자!
// super(); // OK!
// super("model","color"); // OK! (******)
// 매개변수가 있는지 확인하고, 있으면 값을 입력해줘야 한다.
// 매개변수가 없을 경우에는 super();을 생성하지 않아도 괜찮다.
// 매개변수가 없을 경우에는 컴파일러가 자동으로 super();을 넣어주기 때문이다.
// 부모 객체가 자식객체보다 먼저 생성되어야 하기에, 부모 객체를 자식객체보다 먼저 초기화되어야 하기에
// 밑의 위치에서는 생성이 불가능하지만 앞의 위치에는 가능한 것이다.
super(model,color); // (****)
// 부모의 객체를 완전히 초기화하기 위해서 super();이 아니라 super(model,color);로 작성하였다.
// 부모 객체의 초기화는 super();을 통해 이루어져야 한다.
// 이 방법으로 초기화를 해야하는데, 부모객체에서 오버로딩을 한 후
// 자식객체에서 필요에 따라 초기화할 필요가 있다.
System.out.println("DmbCellPhone (String model, String color, int channel) invoked");
// 부모 초기화
// this.model = model; // model과 color의 필드가 자식에게는 없지만 부모에게 있기에 사용이 가능하다.
// this.color = color; // model과 color의 상속이 이루어진 것이다.
// 사실 부모 객체는 super을 통해서 초기화시켜 줄 수 있도록 해야 한다.
// 자식 초기화
this.channel = channel;
// super(); // 이 위치에서는 컴파일 오류가 발생한다.
System.out.println("\t + 1. this(자식객체의 주소) : " + this);
// System.out.println("\t + 2. super(부모) : " + super);
} // constructor
// 메소드도 상속받은 상태이다.
// 부모자식관계는 Type Hierachy에서 볼 수 있다.
// 3. 메소드
void turnOnDmb() {
System.out.println("채널" + channel + "번 DMB 방송 수신을 시작합니다.");
} // turnOnDmb
void changeChannel(int channel) {
this.channel = channel;
System.out.println("채널 " + channel + "번으로 바꿉니다.");
} // changeChannel
void turnOffDmb() {
System.out.println("DMB 방송 수신을 멈춥니다.");
} // turnOffDmb
} // end class
package Practice;
public class DmbCellPhoneExample {
public static void main (String [] args) {
// 1. 자식 DmbCellPhone 객체 생성
DmbCellPhone dmbCellPhone = new DmbCellPhone("자바폰", "로즈골드",10);
// 2. CellPhone으로부터 상속 받은 필드를 사용가능 한가? - 상속재산은 전부 내꺼!!
System.out.println("모델 : " + dmbCellPhone.model);
System.out.println("색상 : " + dmbCellPhone.color);
// 3. 자식 클래스에서만 선언된 DmbCellPhone의 필드의 사용 - 당연히 OK!!
System.out.println("채널 : " + dmbCellPhone.channel);
// 4.
dmbCellPhone.powerOn();
dmbCellPhone.powerOff();
dmbCellPhone.sendVoive("여보세요!");
dmbCellPhone.receiveVoice("안녕하세요! 저는 민주인데요!");
dmbCellPhone.sendVoive("반갑습니다!");
dmbCellPhone.hangUp();
// 5.
dmbCellPhone.turnOnDmb();
dmbCellPhone.changeChannel(12);
dmbCellPhone.turnOffDmb();
} // main
} // end class
03. 상속 - 재정의
package Practice;
public class Calculator01 {
double areaCircle ( double r ) {
System.out.println("Calculator01::areCircle(r) invoked.");
// Calculator01::areCircle(r)의 의미는 Calculator01객체의 areCircle(r)이라는 의미이다.
return 3.14159 * r * r;
} //areaCircle
} // end class
package Practice;
public class Computer extends Calculator01 {
@Override
double areaCircle (double r) {
System.out.println("Computer::areaCircle(r) invoked");
return Math.PI * r * r;
} // areaCircle
// 이렇게 재정의 하면 Calculator01.areacircle해도 부모클래스 메소드가 아닌 자식 클래스에서 재정의 된 메소드가 실행된다.
} // end class
package Practice;
public class ComputerExample {
public static void main(String [] args) {
int r = 10;
Calculator01 calculator = new Calculator01();
System.out.println("1. 원면적 : " + calculator.areaCircle(r));
// 결과 : Calculator01::areCircle(r) invoked. 1. 원면적 : 314.159
System.out.println();
// ===========================================================================
Computer computer = new Computer();
System.out.println("2. 원면적 : " + computer.areaCircle(r));
// 결과 :Computer::areaCircle(r) invoked 2. 원면적 : 314.1592653589793
System.out.println();
// ============================================================================
} // main
} // end class
04. 상속 - 재정의 2
package Practice;
public class Airplane {
public void land() {
System.out.println("Airplane::land() invoked");
} // land
public void fly() {
System.out.println("Airplane::fly() invoked");
} // fly
public void takeOff() {
System.out.println("Airplane::takeOff() invoked");
} // takeOff
} // end class
package Practice;
public class SupersonicAirplane extends Airplane {
// 1. 불변의 진리값 (상수) 선언 : 공용으로 사용
public static final int NORMAL = 1;
public static final int SUPERSONIC = 2;
public int flyMode = NORMAL; // 비행모드 : NORMAL / SUPERSONIC
@Override
public void fly() {
System.out.println("SupersonicAirplane::fly() invoked");
if(flyMode == SUPERSONIC) {
System.out.println("초음속 비행중입니다.");
} else {
super.fly();
} // if -else
} // fly
} // end class
package Practice;
public class SupersonicAirplaneExample { // 재정의
public static void main ( String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.takeOff(); // 이륙
sa.fly(); // 비행
sa.flyMode = SupersonicAirplane.SUPERSONIC; // 초음속 모드로 변경
sa.fly(); // 비행
sa.flyMode = SupersonicAirplane.NORMAL; // 정상 모드로 변경
sa.fly(); // 비행
sa.land(); // 착륙
// ===========================================================================
// 결과
/*
*
*
Airplane::takeOff() invoked
SupersonicAirplane::fly() invoked
Airplane::fly() invoked
SupersonicAirplane::fly() invoked
초음속 비행중입니다.
SupersonicAirplane::fly() invoked
Airplane::fly() invoked
Airplane::land() invoked
*
*
**/
} // main
} // end class
05. 상속 - 오버라이딩을 통한 재정의 3
package Practice;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Animal {
public void sound() {
System.out.println("Animal::sound() invoked.");
} // sound
} // end class
package Practice;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog::sound() invoked.");
System.out.println("멍멍!!");
} // sound
} // end class
package Practice;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat::sound() invoked.");
System.out.println("냐옹!!");
} // sound
} // end class
package Practice;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat::sound() invoked.");
System.out.println("냐옹!!");
} // sound
} // end class
package Practice;
public class App { // 오버라이드를 통한 다형성 (****)
public static void main (String[] args) {
// 다형성 1 : 상속관계의 두 참조타입이 있을 때 자식타입의 객체가 부모타입의 변수에 대입이 가능하다.
// ( 왜? 부모가 자식보다 크니까 )
// 주의사항 : 부모는 단순 직계존속뿐만이 아니라 조상인 object까지 포함한다.
Animal animal = new Dog(); // 다형성 1
// 부모타입 변수명 = new 자식타입(); (****)
// 부모타입이 자식타입으로 초기화
// Lvalue와 Rvalue가 다른데 어떻게 초기화된 것일까?
// 기본타입이 아니라서 큰타입 작은타입 비교도 어려울텐데??
// --> 부모타입이 자식타입보다 크기 때문에 초기화가 가능하다!! (**)
// --> 부모자식의 상속을 통해서 누가 크고 누가 작은지 파악할 수 있다.
// --> 부모 자식 간에는 크기가 존재해서 자동형변환이 발생한다.
// 즉, 컴파일러가 dog를 animal의 크기로 자동형변환하여 초기화시킨 것이다.
// 그렇다고 해서 애니멀이라는 그릇안에 도그가 있는 것이지
// 도그가 애니멀로 완전히 변한 것은 아니다.
// 그렇기 때문에 부모 애니멸의 메소드를 오버라이드한 도그의 메소드가 실행된다.
animal.sound(); // 다형성 2
// 출력 : Dog::sound() invoked. 멍멍!!
// 다형성 2 :
// 전제조건( 다형성 1 )을 충족한다면, 부모타입의 참조변수의 메소드를 호출했을 때 부모타입 객체의 메소드가 호출되는 것이 아니라,
// 부모로부터 상속받은 메소드가 "오버라이딩" 되었을 때, 이 "오버라이딩"된 메소드가 무조건 호출된다.
// ===============================================
animal = new Cat(); // 다형성 1
animal.sound(); // 다형성 2
// 출력 : Cat::sound() invoked. 냐옹!!
// 같은 변수 같은 메소드임에도 불구하고 다른 출력결과가 나타났다.
// 부모 클래스에 있는 것이 아니라, 자식객체에 오버라이딩된 메소드가 출력되게 된다.
} // main
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 16일차 - 추상 클래스 및 인터페이스 (0) | 2022.03.21 |
---|---|
KH 15일차 - protected 접근제한자 및 프로모션 (0) | 2022.03.18 |
KH 13일차 - 어노테이션 (0) | 2022.03.16 |
KH 12일차 - 접근제한자 및 어노테이션 [ + 1차 시험 후기 ] (0) | 2022.03.14 |
KH 11일차 - 정적 초기화 블록과 상수 + 간단한 10일 후기 (0) | 2022.03.11 |