자바-6(상속)

dragonappear·2021년 3월 14일
0

Java

목록 보기
6/22

# 학습내용

1.자바 상속의 특징:

상속이란?

부모 클래스에서 정의된 필드와 메소드를 자식 클래스가 물려받는 것이다.
상속은 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드 중복을 줄여준다.
부모 클래스의 수정으로 모든 자식 클래스들의 수정 효과를 가져오기 때문에 유지 보수 시간도 작아진다.


출처:링크텍스트

subclass에서 상속을 사용하는법:

클래스 선언부에서 extends키워드를 사용하면 된다.

class MountainBike extends Bicycle{
	// new fields and methods defining
    // a mountain bike would go here
}

상속을 사용하면 MountainBike는 Bicycle과 같은 필드와 메소드를 가지게 된다.

상속이 필요한 이유

  1. 공통된 특징을 가지는 클래스 사이의 멤버(필드,메소드) 선언이 불필요하다.
  2. 부모클래스의 멤버(필드,메소드)를 재사용함으로써 자식 클래스가 간결해진다.
  3. 클래스간 계층적 분류 및 관리가 쉬워진다.

상속의 특징

  1. 다중상속을 지원하지 않는다. extends 뒤에는 단 하나의 부모 클래스만 올수있다.
  2. 자바에서는 상속의 횟수에 제한이 없다.
  3. C++의 경우에는 최상위 클래스가 없지만, 자바에서는 최상위 클래스가 Object class이다.
  4. 부모의 메소드와 변수만 상속되며, 생성자는 상속되지 않는다.(부모의 메소드는 재정의하여 사용가능하다. -> 오버라이딩)
  5. 부모의 private 접근 제한을 갖는 멤버는 상속 불가능하다.
  6. final 클래스는 상속할수없고, final 메소드는 오버라이딩이 불가능하다.
public class Parent{
	//부모클래스
    public void print(){
    	System.out.println("A");
    }
}

public class Child extends Parent{
	// Parent를 상속받는 클래스 Child 선언
    public void print(){
    // 메소드 오버라이딩 - A를 상속받았으나 함수를 재정의
    System.out.println("B");}
    }
}

public class Test{
public static void main(String[] argss{
    	Child c = new Child();
        c.print() // B를 출력
    
    }
}

오버라이딩은 부모의 함수를 재정의한다. 그렇기 때문에 함수명,리턴값,파라미터가 모두 동일해야한다.

메소드 오버로딩

public class A{
	public void print(){
    	System.out.println("A");
    }
    
    public void print(int a){
    	System.out.println("A"+"-"+a);
    }
    
    public void print(int a,int b){
    	System.out.println("A"+"-"+a+"-"+b);
    }

}

오버로딩은 메소드와 리턴값은 같지만 매개변수가 다른것을 의미한다. 매개변수의 타입이 달라져도 오버로딩이 성립된다.

2.super 키워드

super 키워드:

super의 경우 this와는 다르게 자식클래스가 부모클래스로부터 상속받은 멤버를 사용하고자할때 사용할수있다.

super.멤버번수

class Parent{
   int a = 10;
}

class Child extends Parent{
    int a = 20;
    
    void childMethod(){
           System.out.println(a);         //20
           System.out.println(this.a);     //20
           System.out.println(super.a);   //10
    }
}

자식이 부모 메소드를 호출해야하는 경우도 super 키워드를 사용할수있다.
부모 객체를 참조하고 있기 때문에 멤버와 메소드에 직접 접근이 가능하다.

super.부모메소드();

public class Airplane {
  public void land() { }
  public void fly() {
    System.out.println("일반 비행");
  }
  public void takeOff() { }
}

public class SupersonicAirplane extends Airplane {
  public static final int NORMAL = 1;
  public static final int SUPERSONIC = 2;

  public int flyMode = NORMAL;

  @Override
  public void fly() {
    if (flyMode = SUPERSONIC) {
      System.out.println("초음속 비행");
    } else {
      // 부모 메소드 호출
      super.fly();
    }
  }
}

super();

부모의 기본 생성자를 호출하는 역할( 반드시 자식 생성자의 첫줄에 위치)

super()는 자식클래스의 기본생성자에 생략되어있지만 들어있다.

자바는 자식 객체를 생성하면 부모 객체가 먼저 생성되고 자식 객체는 그 다음에 생성되는 구조
이다.

객체를 생성하려면 생성자를 호출해야 하는데 부모 클래스의 생성자를 호출한 곳은? 자식 생성자의 맨 첫줄

자식클래스의 생성자를 명시적으로 선언하지 않았다면 컴파일러가 자동으로 다음과 같은 기본 생성자를 생성

public Child(){
super();
}

자식 클래스에서 super()l를 명시적으로 작성하지 않아도 컴파일러가 자동으로 추가

package week6;

public class Parent {
    public int num ;

    Parent(){
        System.out.println("Super class1");
        num=1;
    }

    Parent(int num){
        System.out.println("Super class2");
        this.num=num;
    }
}
package week6;

public class Child extends Parent {
    public Child(){
    	// super() 부모클래스의 기본생성자 자동호출
        // super() 숨겨져있는것
    };

    public Child(int num){
        super(num);
    }

    public static void main(String[] args) {
        Child c1 = new Child();
        Child c2 = new Child(5);
        System.out.println(c1.num);
        System.out.println(c2.num);
    }
}
Super class1
Super class2
1
5

부모 클래스에 기본생성자가 없고 매개변수를 갖는 생성자만 있다면 자식 클래스에서 반드시 super(매개값,,);를 직접 명시

public class People{
	public String name;
    public String ssn;
    
    public People(String name, String ssn){
    this.name = name;
    this.ssn = ssn;
    }
}
public class Student extends People{
	public int studentNo;
    
    public Student(String name, String ssn, int studentNo){
    super(name,ssn);
    this.studentNo = studentNo;
    }
}

부모 클래스 People에는 기본 생성자가 없기 때문에 자식 클래스 student에서 반드시 매개값을 갖는 super(name,ssn)을 호출

super(name,ssn); 를 작성하지 않은 경우 컴파일러 에러 발생

3.메소드 오버라이딩

부모 클래스로부터 상속받은 메소드를 재정의 하는것을 말한다.

부모클래스의 메소드의 이름,매개변수,반환타입이 같아야한다.

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

부모클래스의 메소드보다 많은 수의 예외를 선언할 수 없다.

인스턴스 메소드와 static 메소드를 서로 변경할수없다.

@Override으로 오버라이딩이 정확하게 재정의됐는지 검사할수있다.

public class Parent{
	protected int method1(int num1,int num2){
   	return num1/num2; 
   }
}

class Child extends Parent{
	@Override // 오버라이딩 검사
    public int method1(int num1,int num2)// 접근제어나 넓은 범위로 변경해야한다.{
    	return num1*num1,num2*num2;
    }
}

4.다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

메소드 디스패치란?

메소드 디스패치는 어떤 메소드를 호출할 지 결정하여 실제로 실행시키는 과정을 말한다.

메소드 디스패치에는 정적 디스패치, 동적 디스패치, 더블 디스패치 세가지가 존재한다.

정적 메소드 디스패치(Static Method Dispatch)

컴팡일시점에서 메소드를 결정하는 과정

public class A{
     public void print(){
           System.out.println("A");
     }
}

public class B extends A {
     
     //메소드 오버라이딩 - A를 상속받았으나 함수를 재정의
     public void print(){
           System.out.println("B");
     }
}

public class Test{
      public static void main(String[] args){
             B b = new B();
             b.print();         //B를 출력 
      }
}

오버라이딩, 오버로딩 역시 정적 메소드 디스패치이다.

메인 함수에서 b.print()를 호출했을때 우리는 클래스 B의 오바리이딩된 함수가 불릴 것이라는 것을 알고있다.

우리가 이미 알고 있는 것과 같이 컴파일러 역시도 이 메소드를 호출하고 실행시켜야 되는 것을 명확하게 알고있는데 우리는 이를 정적 메소드 디스패치라고 부른다.

동적 메소드 디스패치(Dynamic Method)

동적 메소드 디스패치는 정적 디스패치와는 다르게 컴파일러가 어떤 메소드를 호출해야되는지 모르는 것을 말한다.

인터페이스, 추상클래스의 추상메소드 또는 상속을 통한 오버라이딩한 메소드를 호출하는 경우

class A {
  private BB bb;

  A(BB bb) {
    this.bb = bb;
  }

  void print() {
    bb.print();
  }
}

class B implements BB {
  public void print() {
    System.out.println("B");
  }
}

class B1 implements BB {
  public void print() {
    System.out.println("B1");
  }
}

interface BB {
  void print();
}

위의 예제에서 BB라는 추상클래스는 B,B1으로 각각 구현되고 있다. 또한 A라는 클래스는 이런 BB라는 추상클래스(설계도)를 받아 bb.print()라는 함수를 사용하고 있다.

그렇다면 여기서 클래스 A의 print()함수를 사용하면 어떤 함수가 호출될까?
아마도 해당 함수의 객체를 선언할 때에 할당된 Object를 보고 어떤 함수를 실행할지 결정하게 될것이다. 우리가 이렇게 유추가 가능하지만 컴파일러는 이에 대해 알 수 있는 방법이 없다.

즉 컴파일러는 어떤 함수가 실행될지 전혀 모르는 것이다. (B클래스의 print()를 가져와야할지, B1 클래스의 print()를 가져와야 할 지 아는 시점은 런타임 시점일것이다.)

이처럼 컴파일러가 어떤 메소드를 호출해야되는지 모르는 것을 동적 메소드 디스패치라고 한다.

더블 디스패치(Double Dispatch)

더블 디스패치는 Dynamic Dispatch를 두 번 하는 것을 의미한다.

런타임시점에서 메소드를 결정하는 과정이다.

링크텍스트

interface Post{
	void postOn(SNS sns);
}

class Text implements Post{
	public void postOn(SNS sns){
    	sns.post(this);
    }
}

class Picture implements Post{
	public void postOn(SNS sns){
    	sns.post(this);
    }
}

interface SNS{
	void post(Text text);
    void post(Picture picture);
}
class Facebook implements SNS{
	public void post(Text text){
    
    }
    public void post(Picture picture){
    }

}

class Twitter implements SNS{
	public void post(Text text){
    
    }
    public void post(Picture picture){
    }
}

public static void main(String[] args) {
     List<Post> posts = Arrays.asList(new Text(), new Picture());
     List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());

     posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}

위 코드에서 총 2번의 다이나믹 디스패치가 이루어진다.

1) Post에서 어떤 구현체의 postOn을 사용할 지
2) postOn에서 어떤 SNS의 어떤 구현체의 post함수를 사용할지

5.추상 클래스

abstract 키워드를 사용한다
추상클래스는 객체를 생성할수없다.
미완성 클래스이다.
추상메소드(선언부만 작성하고 몸체는 작성안함)가 존재하면 반드시 추상클래스로 정의해야한다.

public abstract class AbstractClass{
	abstract void method1();
    abstract void method2();
    abstract void method3();
}

6.final 키워드

변경될 수 없다는 의미를 갖는다.

public final class FinalClass{ // 상속금지
	final int num= 100; // 값 변경 불가
    
    final void method(){ // 오버라이딩 불가
    	final in num2 = 200; // 값 변경 불가
    }
}

7.Object 클래스

모든 클래스의 부모가 된다(모든 클래스들은 Object 클래스를 상속받는다.)
extends 키워드가 없는 클래스에 컴파일러는 자동적으로 Object클래스를 상속한다.

참고

링크텍스트
링크텍스트
링크텍스트

0개의 댓글