본문 바로가기
개발자 꿈나무의 하루/01_Boot Camp

(네이버클라우드 부트캠프) 26일차 - Java프로그래밍 기초(상속)

by kk_naks 2024. 7. 1.

1. 상속

  1.1 상속의 개념

     - 부모클래스를 상속받은 자식클래스에서 부모클래스의 필드,메소드를 활용 하는 방법을 말한다.

     - 부모클래스의 멤버를 자식클래스에 복사하는것이 아니라, 부모클래스의 멤버를 사용하는 것일 뿐이다. 

     - 직접적으로 바이트 코드를 복사하지 않는다. 



 

  1.2 클래스 상속

     - 클래스 상속 extends를 사용하여 자식클래스가 부모클래스로 부터 상속 받았다는것을 표기한다.

public class Parent{
	필드
    메소드()
}

public class Child extends Parent{
	필드
    메소드()
}

 

  1.3 부모 생성자 호출

     - 클래스 상속을 받은 자식클래스를 인스턴스 하면 부모클래스의 생성자가 자동적(super())으로 호출된다. 

public class SmartPhone extends Phone {
	//자식 생성자 선언
	public SmartPhone(String model, String color) {
		super();
		this.model = model;
		this.color = color;
		System.out.println("SmartPhone(String model, String color) 생성자 실행됨");
	}
}
- 부모클래스의 생성자가 매개변수를 가지고 있다면, 자식클래스도 super(매개변수)의 구조로 사용해야한다. 

 

  1.4 메서드 오버라이딩

     - 자식클래스에서 부모클래스의 메서드를 재정의 하는 방법이다.

- 부모 메서드의 선언부(리턴 타입, 메서드명, 매개변수)는 동일해야한다. 
- 접근제한을 더 강하게 오버라이딩 할수 없다.
- 오버라이딩시 어노테이션(@override)를 사용하면 컴파일 오류를 막을 수 있다.
public class Computer extends Calculator {
	//메소드 오버라이딩
	@Override
	public double areaCircle(double r) {
		System.out.println("Computer 객체의 arearCircle() 실행");
		return Math.PI * r * r;
	}
}

 

  1.5 부모메서드 호출

     - 자식클래스에서 부모클래스의 메서드를 호출 시에는 super.부모메서드명()을 사용한다.

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 {
			//Airpanle 객체의 fly() 메소드 호출
			super.fly();
		}
	}
}

 

  1.6 타입변환

     - 자동 타입변환 : 부모클래스 타입의 변수가 자식 타입의 객체를 사용하면 자동 타입변환이 이 일어난다.

- 부모클래스에 선언된 필드와 메서드만 접근이 가능하다. 
- 자식클래스에 오버라이딩 된 메서드가 있다면 오버라이딩 메소드가 실행된다.
package ch07.sec07.exam02;

public class ChildExample {
  public static class Parent {
    // 메소드 선언
    public void method1() {
      System.out.println("Parent-method1()");
    }

    // 메소드 선언
    public void method2() {
      System.out.println("Parent-method2()");
    }
  }

  public static class Child extends Parent {
    // 메소드 오버라이딩
    @Override
    public void method2() {
      System.out.println("Child-method2()");
    }

    // 메소드 선언
    public void method3() {
      System.out.println("Child-method3()");
    }
  }

  public static void main(String[] args) {
    // 자식 객체 생성
    // Child child = new Child();

    // 자동 타입 변환
    Parent parent = new Child();

    // 메소드 호출
    parent.method1(); //Parent-method1()
    parent.method2(); // child-method2()
    // parent.method3(); (호출 불가능)
  }
}

 

     - 강제 타입변환 : 자식클래스 타입의 변수가 부모클래스의 타입의 변수로 생성되면 강제(캐스팅)을 해야한다.

package ch07.sec07.exam03;

public class ChildExample {
  public static class Parent {
    // 필드 선언
    public String field1;

    // 메소드 선언
    public void method1() {
      System.out.println("Parent-method1()");
    }

    // 메소드 선언
    public void method2() {
      System.out.println("Parent-method2()");
    }
  }

  public static class Child extends Parent {
    // 필드 선언
    public String field2;

    // 메소드 선언
    public void method3() {
      System.out.println("Child-method3()");
    }
  }

  public static void main(String[] args) {
    // 객체 생성 및 자동 타입 변환
    Parent parent = new Child();

    // Parent 타입으로 필드와 메소드 사용
    parent.field1 = "data1";
    parent.method1();
    parent.method2();
    /*
     * parent.field2 = "data2"; //(불가능) parent.method3(); //(불가능)
     */

    // 강제 타입 변환
    Child child = (Child) parent;

    // Child 타입으로 필드와 메소드 사용
    child.field2 = "data2"; // (가능)
    child.method3(); // (가능)
  }
}

 

  1.7 다형성

     - 자동 타입변환과 메서드 오버라이딩으로 인해 다형성이 가능해진다. 

     - 다형성이란 같은 메서드선어부로 다른 결과를 만들수 있는 성질을 말한다. 

- 타이어 굴린다()의 메서드를 상속 받은 한국타이어,금호타이어가 있다고 가정하자.
- 타이어 : 굴린다() = 타이어가 굴러간다.
- 한국타이어 : 굴린다() = 한국 타이어가 굴러간다.
- 금호타이어 : 굴린다() = 금호 타이어가 굴러간다.
- 타이어 t1 = new 타이어 -> t1.굴린다()        = 타이어가 굴러간다.
- 타이어 t1 = new 한국타이어 -> t1.굴린다()  = 한국타이어가 굴러간다.
- 타이어 t1 = new 금호타이어 -> t1.굴린다()  = 금호타이어가 굴러간다. 

 

     - 이러한 성질을 사용하면, 매개변수의 다형성을 만들 수 있다.

public class Driver {
	//메소드 선언(클래스 타입의 매개변수를 가지고 있음)
	public void drive(Vehicle vehicle) {
		vehicle.run();
	}
}

public class Vehicle {
	//메소드 선언
	public void run() {
		System.out.println("차량이 달립니다.");
	}
}

public class Bus extends Vehicle {
	//메소드 재정의(오버라이딩)
	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
}
- 자동 타입 변환 : Vehicle vehicle = new Bus();
- 메서드 오버라이딩 : vehicle.run() - > bus.run()
public class DriverExample {
	public static void main(String[] args) {
		//Driver 객체 생성
		Driver driver = new Driver();
		
		//매개값으로 Bus 객체를 제공하고 driver 메소드 호출
		Bus bus = new Bus();
		driver.drive(bus); 
		//driver.drive(new Bus()); 와 동일
	}
}

 

     - 이것을 응용하면 instanceof와 강제 형변환을 통해 부모클래스의 변수를 자식 클래스의 멤버를 사용할 수 있게 한다. 

  public static void main(String[] args) {
    // 수퍼 클래스의 레퍼런스는 하위 클래스의 인스턴스를 담을 수 있다.
    Vehicle[] arr = new Vehicle[] {new Truck("기아자동차", 5, 10000, 32, 15f, true)};
    printCar(arr[0]);
  }
  // Vehicle v = new Truck("기아자동차", 5, 10000, 32, 15f, true);
  // 부모타입의 변수(v)로 자식타입(Truck)을 선언
  
  static void printCar(Vehicle v) {
    System.out.printf("모델: %s\n", v.model);
    System.out.printf("수용인원: %s\n", v.capacity);
	// v는 car의 부모클래스 
    // car 가능
    if (v instanceof Car) {
      Car c = (Car) v;
      System.out.printf("cc: %s\n", c.cc);
      System.out.printf("밸브: %s\n", c.valve);

      if (v instanceof Sedan) {
        Sedan s = (Sedan) v;
        System.out.printf("썬루프: %b\n", s.sunroof);
        System.out.printf("오토: %b\n", s.auto);
      } else if (v instanceof Truck) {
      	//강제 형변환 
        Truck t = (Truck) v; 
        
        //오버라이딩 메서드 실행
        System.out.printf("톤: %f\n", t.ton);
        System.out.printf("덤프여부: %b\n", t.dump);
      }
    } else if (v instanceof Bike) {
      Bike b = (Bike) v;
      System.out.printf("engine: %b\n", b.engine);
    }
    System.out.println("-----------------------");
  }

}

 

  1.8 추상클래스

     - 추상메서드는 클래스들간에 공통적인 멤버를 선언하되 부모클래스를 직접적으로 사용할 수 없는 클래스이다.

- 추상클래스 이기 때문에 new연산자를 통해 직접적인 생성을 불가능하다.
- 자식클래스에서 상속을 받아 사용 해야한다. 
- 상속 받은 클래스에서는 필드, 메서드 생성자를 사용할 수 있다.
// 추상 클래스 선언
public abstract class Phone {
	//필드 선언
	String owner;
	
	//생성자 선언
	Phone(String owner) {
		this.owner = owner;
	}
	
	//메소드 선언
	void turnOn() {
		System.out.println("폰 전원을 켭니다.");
	}	
    //메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. 
    void internetSearch();
    
	void turnOff() {
		System.out.println("폰 전원을 끕니다.");
	}
}

// 자식 클래스 
public class SmartPhone extends Phone {
	//생성자 선언
	SmartPhone(String owner) {
		//Phone 생성자 호출
		super(owner); //부모클래스의 생성자가 있으므로 생성해야한다. 
	}
	
	//메소드 선언
    //메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. 
	void internetSearch() {
		System.out.println("인터넷 검색을 합니다.");
    
    // 자식 클래스에서 추상클래스의 메서드를 오버라이딩 할 수 있다.
    @override
    void turnOff() {
		System.out.println("스마트폰 전원을 끕니다.");
	}
}

public class PhoneExample {
	public static void main(String[] args) {
		//Phone phone = new Phone();
		
		SmartPhone smartPhone = new SmartPhone("홍길동");
		
		smartPhone.turnOn();         //전원을 킵니다.
		smartPhone.internetSearch(); //인터넷 검색을 합니다.
		smartPhone.turnOff();        //스마트폰 전원을 끕니다.
	}
}

 

  1.9 this와 super

  this super
메서드 현재 객체가 선언된 클래스의 메서드 현재 객체의 부모클래스
필드 현재 객체의 필드 현재 객체의 부모 메서드
package study.oop.clazz;

public class TestThis {

  static class A {
    String name = "A";
    boolean working = true;

    void print() {
      System.out.println("A.print():");
      System.out.printf("  => this.name(%s)\n", this.name);
      System.out.printf("  => this.working(%s)\n", this.working);
    }

    void m1() {

      System.out.println("super.m1() 메서드=> A.m1()");
    }
  }


  static class A2 extends A {
    String name = "A2";
    int age = 20;

    @Override
    void print() {
      System.out.println("A2의 print문");
      System.out.printf("this.name 필드 = > %s\n", this.name);
      this.m1();
      System.out.printf("super.name 필드 = > %s\n", super.name);
      super.m1();
    }

    @Override
    void m1() {
      System.out.println("A2.m1()");
    }
  }


  static class A3 extends A2 {
    String name = "A3";
    String gender = "남";

    @Override
    void m1() {
      System.out.println("this.m1() 메서드=> A3.m1()");
    }
  }

  public static void main(String[] args) {
    A2 obj = new A3();
    obj.print();
  }
}
- 결과
A2의 print문
this.name 필드 = > A2
this.m1() 메서드=> A3.m1()
super.name 필드 = > A
super.m1() 메서드=> A.m1()

- 메서드의 경우 override가 필요하여 A3로 선언된 오버라이딩이 우선
- 필드의 경우 override가 없으니 A2가 우선