Java Syntax - (11) 클래스의 상속과 다형성

가빈·2023년 12월 14일
post-thumbnail

복습시간!
클래스 외부에 올 수 있는 것들 : 패키지, 임포트, 아웃 클래스
클래스 내부에 올 수 있는 것들 : 필드, 메서드, 생성자, 이너클래스

상속

: 부모클래스의 생성자를 제외한 멤버(필드,메서드,이너클래스)를 자식클래스가 내려받아(상속) 클래스 내부에 포함하는 것

UML(Unified Modeling Language)

: 시스템을을 모델로 표현해주는 모델링 언어
상속 다이어 그램을 표기할 때, 자식에서 부모클래스 쪽을 화살표를 표시한다

특징

  • 코드의 중복성 제거

     대학생 - 이름, 나이, 학번(필드) / 밥먹기, 자기, 등교하기(메서드)
     직장인 - 이름, 나이, 사번(필드) / 밥먹기, 자기, 출근하기(메서드)
     
     공통부분 사람: 이름, 나이, 밥먹기, 자기 -> 부모클래스
    차이점 : 학생: 학번, 등교하기 -> 자식클래스1 
            회사원 : 사번, 출근하기 -> 자식클래스2
  • ⭐️⭐️ 상속으로 인해 다형적 표현 가능

     사과는 과일이다 ( 과일 fruit = new 사과() )
      포도는 과일이다 ( 과일 fruit = new 포도() )
      키위는 과일이다 ( 과일 fruit = new 키위() )

상속 문법

  1. extends(확장) 키워드 사용
    class 자식클래스 extends 부모클래스 {...}

  2. 다중 상속 불가
    class 자식클래스 extends 부모클래스 1, 부모클래스 2{...}
    => 불가능 부모가 둘일 수는 없지만 자식은 여러개 가능

    class Human{
         String name;
         int age;
    
         void eat(){...}
         void sleep(){...}
    }
    
    class Student extends Human {
        int studentID;
        void goToSchool(){...}
    }
    class Worker extends Human {
        int workerID;
        void goTowork(){...}
    }

상속 후 메모리 구조

  1. 부모 객체가 있는 경우, 부모 객체가 먼저 생성
    -> 부모클래스가 먼저 만들어지고 이것을 포함하는 자식클래스가 클래스 영역에 생성
    -> 부모클래스 객체와 자식클래스 객체(생성자)가 힙에 저장된다
    -> 힙을 가리키는 참조변수가 stack에 저장

생성자의 상속여부

생성자를 제외하고 상속된다고 했는데 상속할거면 다 하지 이게 무슨 일??
다음과 같은 이유로 상속되지 않는다

⭐️생성자의 두가지 조건

  1. 클래스 이름과 동일
  2. 리턴타입 없음
class A{
	A() {
    
    }
}

class B extends A {
 	A() {
    //만약 생성자가 들어왔다면 클래스 이름과 동일해야하는데-> 클래스 B이므로 안됨
    //그러면 메서드여야 하는데 메서드는 리턴타입이 존재
    }
}


객체의 타입변환

복습시간!
기본자료형에서는 값의 범위를 기준으로 했다
int -> double 업캐스팅 = 자동
double -> int 다운캐스팅 = 수동

업캐스팅과 다운캐스팅

기본자료형과 달리, 상속으로 기준으로 타입변환이 이루어진다

업캐스팅 : 자식 -> 부모

  • 자연스러운 방향이라 항상 가능
    A a = new (A) b;-> 컴파일러가 자동 업캐스팅

⭐️⭐️다운캐스팅 : 부모 -> 자식

  • 가능할 때도 있고 안 될 때도 있음
  • 방향을 거스르는 것이기 때문에 수동으로 진행
    즉, heap메모리에 객체가 존재해야한다

    B b = new (B) a;
    -> 우변의 타입이 a일지라도 참조변수 b가 객체 b를 가리키고 객체 b는 a의 정보들을 상속 받아서 가능한 일이다

좀 더 자세히 알아보면,

class A {
	int m=3;
    void abc() {
    	System.out.println("A");
    }
}
class B extends A {
	int n=4;
    void bcd() {
    	System.out.println("B");
    }
}

1) B로 선언타입이 된 경우

B b = new B();
System.out.println(b.m);
//가능 b가 B객체를 가리키고 있고 B객체는 A를 상속받음
System.out.println(b.n);
//자기클래스의 필드이므로 가능

b.abc();// 같은 이유로 가능
b.bcd();// 같은 이유로 가능

2) A로 선언타입이 된 경우

 A a = new A();
 System.out.println(a.m);
 //가능 A의 자기 클래스 내의 필드임
 System.out.println(a.n);
 //불가능 참조변수 a는 자신의 객체인 A를 가리키고 있기 때문에 B의 내용을 상속받지 않고 내용도 없음
 
 a.abc();//가능 자기클래스의 메서드
 a.bcd();//불가능 B클래스의 메서드라 위와 같은 내용으로 불가능

instanceof 타입

: 다운캐스팅 가능여부 확인하는 참조변수 Boolean 타입

// 다운 캐스팅이 가능한 여부를 확인해보자

A a = new B();
if( a instanceof B ) {
	B b = new (B)a;//true
}


인스턴스 메서드 오버라이딩(overriding)

: 부모 클래스에게 상속받은 메서드를 재정의하여 사용

재정의 조건

  1. 부모클래스의 메서드와 시그니처(메서드 이름,변수타입)및 리턴타입 동일

  2. 부모클래스의 메서드보다 접근지정자는 같거나 넓어야 함->그렇지 않으면 오버라이딩 의미가 없음


class A {
  	void print() {
  		System.out.println("A클래스");
  	}
  }
class B extends A {
  	void print() {//A와 동일한 메서드
  		System.out.println("B클래스");//B 클래스로 나온다
  	}
public static void main(String[] ar) {
  
  		A aa = new A();//A 클래스만들고 스택에 aa가 가르키는 곳에 A객체 만들어라
  		aa.print();//A클래스
  
  		B bb = new B();//B 클래스만들고 스택에 bb가 가르키는 곳에 B객체 만들어라
  		bb.print()://B클래스
  
  		A ab = new B();//스택에 ab가 가르키는 곳에 A를 품고 있는 B를 만들어라-> 오버라이딩
  		ab.print();//B클래스
  	}
  }

유형

1) 각각 타입으로 선언 + 각각 객체 생성

2) animal타음으로 선언 + 자식클래스로 객체 생성(다형성)

Animal ab = new Bird();
Animal ac = new Cat();
Animal ad = new Dog();

ab.cry(); //짹짹
ac.cry(); //야옹
ad.cry(); //멍멍

3) 배열로 관리

Animal[] animals = new Animal[] {new Bird(), new Cat(), new Dog()};
  for(Animal animal : animals) {
    animal.cry();
  //for-each문을 이용해서 animals 집합에 해당하는 원소면 출력가능
  //배열로 한번에 관리할 수 있다
  }

❗️무엇으로 생성했는지가 중요하다

메서드 오버라이딩 VS 메서드 오버로딩

오버라이딩 : 상속에서 중복 (다 똑같음)
오버로딩 : 메서드명과 리턴타입만 같고 매개변수 다름


instance 필드와 static 멤버의 중복

  1. instance 필드도 오버라이딩 될까?
    -> 오버라이딩 XXX, 제일 먼저 만나는 값으로 출력된다

    class A {
         int m=3;
      }
    class B extends A {
         int m=4;
      }
    
    A ab = new B();
     //A클래스가 있고 참조변수는 ab, 객체는 클래스A이므로 A 객체를 가리킨다
    ab.m; //3

  2. static 필드도 오버라이딩 될까?
    -> 오버라이딩 XXX, 인스턴스 필드와 마찬가지로 먼저 만나는 값으로 출력된다

  3. static 메서드도 오버라이딩 될까?
    -> 똑같다 이렇게 했는데도 모르면,,, 돌아가라

❗️어떤 클래스로 되어있는지가 중요하다


super와 super()

super 키워드

: this 키워드는 자기 클래스의 객체 호출이었다면
super는 부모클래스의 객체 가리키는 것
-> 부모의 필드와 메서드를 호출하기 위해서 사용(상속에서의 this키워드 개념과 유사)

  class A {
    void abc(){
        System.out.println("A클래스 abc()");
    }
  }

  class B extends A{
    void abc() {
        System.out.println("B클래스 abc()");
  //인스턴스 메서드 오버라이딩
    }

    void bcd() {
     super.abc(); //A클래스 abc()
  //A클래스의 void abc()를 출력
    }
  }

super()

: this()는 자신의 생성자를 호출했다면 super()는 부모클래스의 생성자를 호출하는 것

  • 생성자 내부에서만 사용가능
  • 반드시 중괄호 이후 첫 줄에 위치
  • 자식클래스 생성자의 첫 줄에는 반드시 this()또는 super()가 포함되어야 함(생략시 super()자동추가)
    class A {
      A(){
          System.out.println("A클래스 생성자");
      }
    }

    class B extends A{
      B() {
  		  super();
          System.out.println("B클래스 생성자");
 		  //A클래스 생성자와 B클래스 생성자 출력
      }
    }


최상위 클래스 Object

: 모든 자바 클래스의 부모클래스 = 자바의 모든 클래스는 Object의 메서드를 가짐
❗️ 즉, Object클래스를 제외하고는 모든 클래스는 상속이 되어있을 수밖에 없다.

class A extends Object{
    //상속하지 않아도 자동으로 extends Object를 추가
  }
class B extends A {
  }

  System.out.pritnln("블라블라");
  // 아무렇게나 적어도 오류가 안 난 이유는 이것도 Object클래스에 속해 있기 때문이다

모든 클래스가 가지는 object 메서드(당장은 no.)

toString()

: 객체의 정보를 알려줌(어디 패키지이고 뭐 어디 클래스 소속인지)패키지.클래스명@해쉬코드
-> 잘 안써서 오버라이딩해서 많이들 씀

class A { //toString()을 출력하면 Object의 객체정보 출력
	int a=3;
  int b=4;
}
class B { //object클래스와 상속 overriding이 됨
	int a=3;
  int b=4;

  @Override
  public String toString() {
  	return "필드값": a="+a + ", b="+b;

	}
}
public static void main(String[] ar) {
	A aa = new A();
	System.out.prinf("%x\n",aa.hashCode());
	System.out.prntln(aa); //aa.toString 자동실행

	B bb = new B();
	System.out.println(bb);//bb.toString 자동 실행 오버라이딩 했던 값이 나옮
}

equals()

: 객체의 stack 메모리 주소값을 비교(비교연산자와 동일한 결과)

class A { 
	String name;
	A(String name) {
		this.name=name;
	}
}
class B {
	String name;
	B(String name) {
		this.name=name;
	}
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof B) {//B가 obj로 다운캐스팅이 가능하다면
			if (this.name == ((B)obj).name)
		//B다운캐스팅된 obj의 name과 이 B클래스의 name이 같다면 
				return true 
		}
		return false;
	}

}
public static void main(String[] ar) {
	A aa1 = new A("안녕");
	A aa2 = new A("안녕");

	System.out.println(aa1==aa2);//false 다른 객체
	System.out.pritnln)(aa1.equals(aa2)); //false 다른 객체

	B bb1 = new B("안녕");
	B bb2 = new B("안녕");

	System.out.println(bb1==bb2); //false
	System.out.pritnln)(bb1.equals(bb2)); //true

hashCode()

: 객체의 hashCode값 리턴(해쉬의 자료구조들 중 그 비교값을 리턴해준다)

hashMap : 데이터를 쌍으로 구성함 (key,value)
key는 절대 중복 되지 않음 -> key값이 서로 같은지를 확인하는 프로세스

  1. hashCode() 동일한지
  2. equal() 결과가 true인지
    둘다 만족해야 같은 객체이다

  • 정리

    반환타입메서드명기능
    StringtoString()object의 객체정보 : package.클래스명@해쉬코드-> 일반적으로 오버라이딩 한다
    booleanequals(Object obj)매개변수 Obj 객체와 stack 메모리 주소를 비교 -> 비교연산자와 동일한 결과
    inthashCode()위치값을 기반으로 생성된 고유값: 객체의 hashCode()값을 리턴 / hashTable,hashMap 동등비교에 사용 [1. hashCode()일치 2. equals() true -> 동등]
    voidwait() / wait(long timeout) / wait(long timeout, int nanos)현재 쓰레드를 일시정지한 상태로 전환 : 동기화 블록에서만 사용
    voidnotify() / notifyAll()wait()을 통해서 하나또는 전체 쓰레드를 일시정지 해제 : 동기화 블록에서만 사용
profile
지금부터 시작!

0개의 댓글