티스토리 뷰

7.1 상속 개념

  • 부모 클래스의 필드와 메소드를 자식 클래스에게 물려줄 수 있음
  • 잘 개발된 클래스를 재사요해서 새로운 클래스를 만들기 때문에 중복 코드를 줄일 수 있음
  • 부모 클래스만 수정하면 되기 때문에 수정을 최소화할 수 있음

 

7.2 클래스 상속

  • 자식이 상속 받으려고 하는 부모 클래스를 선택
  • 다중 상속을 허용하지 않음

 

7.3 부모 생성자 호출

자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성 된다.

부모 생성자는 자식 생성자의 맨 첫줄에 숨겨져 있는 super() 에 의해 호출된다.

 


public class SmartPhoneDemo {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone("아이폰", "은색");

        System.out.println(smartPhone.color); // 은색 출력
        
        // 부모 메서드 호출
        smartPhone.bell();
        
        // 자식 메서드 호출
        smartPhone.internet();
    }
}

// 부모 클래스
class Phone {
    public String model;
    public String color;

// 기본 생성자는 암묵적으로 제공

    public void bell(){
        System.out.println("벨이 울립니다");
    }
}

// Phone를 상속 받은 자식 클래스
class SmartPhone extends Phone {
    public boolean wifi;
    
    // 부모 클래스에서 상속받은 필드로 생성자 선언
    public SmartPhone(String model, String color) {
    // 기본 생성자만 있을 경우 super() 생략 가능 컴파일러가 자동으로 추가
        this.model = model;
        this.color = color;                
    }
    
    public void internet(){
        System.out.println("인터넷 연결합니다");
    }
}

 

// 부모 클래스
class Phone2 {
    public String model;
    public String color;

    // 부모 클래스에 매개변수가 있는 생성자가 있을 경우 
    public Phone2(String model, String color) {
        this.model = model;
        this.color = color;
    }
}

// Phone를 상속 받은 자식 클래스
class SmartPhone2 extends Phone2 {
    public boolean wifi;

    public SmartPhone2(String model, String color) {
       // 부모 클래스에 매개변수가 있는 생성자가 있을 경우에는 반드시 호출해줘야 한다. 
        super(model, color);
    }
}

 

7.4 메소드 재정의

메소드 오버라이딩

  • 부모 클래스의 메소드를 자식 클래스에서 재정의해서 사용하는 것
  • 메소드가 오버라이딩 되었다면 해당 부모 메소드는 숨겨지고 자식 메소드가 우선적으로 사용
  • 부모 메소드의 선언부(리턴 타입, 메소드 이름, 매개변수) 동일
  • 접근 제한을 더 강하게 오러바이딩할 수 없음 (public -> private 변경 불가)
  • 새로운 예외를 throws할 수 없음
public class OverridingDemo {
    public static void main(String[] args) {
        Child child = new Child();

        child.method1(); // 부모 메소드 1 출력
        child.method2(); // 자식 메소드 2로 오버라이딩 출력
    }
}

class Parent {
    public void method1(){
        System.out.println("부모 메소드 1");
    }

    public void method2(){
        System.out.println("부모 메소드 2");
    }
}

class Child extends Parent {
    @Override // 컴파일 시 정확히 오버라이딩 되었는지 체크해주며 생략 가능
    public void method2() {
        System.out.println("자식 메소드 2로 오버라이딩");
    }
}

 

부모 메소드 호출

  • 부모 메소드의 일부만 변겨된다 하더라도 중복된 내용을 자식 메소드도 가지고 있어야 한다. 
  • super. 사용하면 숨겨진 부모 메소드를 호출할 수 있음

 

7.5 final 클래스와 final 메소드

  • 클래스를 선언할 때 final를 사용하면 더 이상 상속할 수 없는 클래스가 된다. -> 자식 클래스를 만들 수 없음      
  • 메소드를 선언할 때 final를 사용하면 오버라이딩할 수 없는 메소드가 된다. 

 

7.6 protected 접근 제한자 

  • 같은 패키지에서는 접근 가능
  • 다른 패키지에서는 자식 클래스만 접근 가능
  • 단 다른 패키지에서는 new연산자를 사용해 생성자를 직접 호출할 수는 없고 super()를 사용해야 한다.

 

7.7 타입 변환

자동 타입 변환:  부모 타입 변수 = 자식 타입 객체

  • 자식은 부모의 특징과 기능을 상속 받기 때문에 부모와 동일하게 취급될 수 있음
  • 바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환 가능
  • 부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 사용 가능
  • 그러나 자식 클래스에서 오버라이딩된 메소드가 있다면 부모 메소드 대신 오버라이딩된 자식 메소드가 호출 된다.
package beginnerJava.src.ch07;

public class ChildDemo {
    public static void main(String[] args) {
        // 자식 객체 생성
        Child2 child2 = new Child2();

        // 부모 타입으로 자동 타입 변환
        Parent2 parent2 = child2;
        
        parent2.method1(); //부모 메소드 1 출력
        parent2.method2(); //자식 메소드 2로 오버라이딩 출력

        // 호출 불가능
//        parent2.method3();

    }
}

class Parent2 {
    public void method1(){
        System.out.println("부모 메소드 1");
    }

    public void method2(){
        System.out.println("부모 메소드 2");
    }
}

class Child2 extends Parent2 {
    @Override
    public void method2() {
        System.out.println("자식 메소드 2로 오버라이딩");
    }

    public void method3(){
        System.out.println("자식 메소드 3");
    }
}

 

강제 타입 변환: 자식 타입 변수 = (자식 타입) 부모 타입 객체

  • 부모 타입은 자식 타입으로 자동 변환되지 않고 자식 타입의 캐스팅 연산자를 사용해 강제 타입 변환을 할 수 있음
  • 자식 객체가 부모 타입으로 자동 변환된 후 다시 자식 타입으로 변환될 때 강제 타입 변환 사용 가능
package beginnerJava.src.ch07;

public class ChildDemo2 {
    public static void main(String[] args) {
        // 부모 타입으로 자동 타입 변환
        Parent3 parent3 = new Child3();
        
        parent3.method1();
        parent3.method2();
        
        // 자식 타입 메소드 사용 불가능
//        parent3.method3();
        
        // 자식 타입으로 강제 타입 변환
        Child3 child3 = (Child3) parent3;
        
        // 자식 타입 메소드 사용 가능
        child3.method3();

    }
}

class Parent3 {
    public void method1(){
        System.out.println("부모 메소드 1");
    }

    public void method2(){
        System.out.println("부모 메소드 2");
    }
}

class Child3 extends Parent3 {
    @Override
    public void method2() {
        System.out.println("자식 메소드 2로 오버라이딩");
    }

    public void method3(){
        System.out.println("자식 메소드 3");
    }
}

 

7.8 다형성

  • 다형성이란 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질
  • 자동 타입 변환 + 메소드 오버라이딩 -> 다형성

 

필드 다형성

  • 필드 타입은 동일하지만 (사용 방법은 동일하지만)
  • 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것
public class CarDemo {
    public static void main(String[] args) {
        Car hankookCar = new Car();
        hankookCar.tire = new HankookTire();
        
        Car kumhoCar = new Car();
        kumhoCar.tire = new HankookTire();

        // run() 함수를 호출하는 것은 한국도 금호도 동일하다. (사용 방법 동일)
        // 하지만 대입되는 객체가 다르기 때문에 실행 결과가 다르게 나온다. (실행 결과 다름)
        hankookCar.run(); // 한국타이어가 회전 출력
        kumhoCar.run(); // 금호타이어가 회전 출력
    }
}

class Car {
    Tire tire;

    public void run(){
        tire.roll();
    }
}

class Tire {
    public void roll(){
        System.out.println("타이어가 회전");
    }
}

class HankookTire extends Tire {
    @Override
    public void roll() {
        System.out.println("한국 타이어가 회전");
    }
}

class KumhoTire extends Tire {
    @Override
    public void roll() {
        System.out.println("금호 타이어가 회전");
    }
}

 

메서드 다형성

  • 메소드가 클래스 타입의 매개변수를 가지고 있을 때, 
  • 매개 변수 타입의 자식 객체를 제공할 수도 있다는 것 = 어떤 자식 객체를 제공하냐에 따라 실행 결과가 달라짐
public class DriverDemo {
    public static void main(String[] args) {
        Driver busDriver = new Driver();
        Bus bus = new Bus();

        Driver taxiDriver = new Driver();
        Taxi taxi = new Taxi();

        // 똑같은 Vehicle 타입을 매개변수로 삼고 있지만
        // bus 객체냐 taxi 객체냐에 따라 그 실행 결과가 달라지는 것
        busDriver.drive(bus); // 버스가 달립니다 출력
        taxiDriver.drive(taxi); //  택시가 달립니다 출력

    }
}

class Driver {
    public void drive(Vehicle vehicle) {
        vehicle.run();
    }
}

class Vehicle {
    public void run(){
        System.out.println("달립니다");
    }
}

class Bus extends Vehicle {
    @Override
    public void run() {
        System.out.println("버스가 달립니다");
    }
}

class Taxi extends Vehicle {
    @Override
    public void run() {
        System.out.println("택시가 달립니다");
    }
}

 

7.9 객체 타입 확인

  • instanceof 연산자: 변수가 참조하는 객체의 타입을 확인하고자 할 때 사용
public class InstanceofDemo {
    public static void personInfo(Person person) {
        System.out.println("이름은 " + person.name);

        if (person instanceof Student) {
            System.out.println("이 사람은 학생입니다");
        }
    }

    public static void main(String[] args) {
        Person person = new Person("김자바");
        personInfo(person); // 이름은 김자바 까지만 출력

        Student student = new Student("이학생", "1234567890");
        personInfo(student); 
        //이름은 이학생
        //이 사람은 학생입니다  2 줄 출력
    }
}

class Person {
    public String name;

    public Person(String name) {
        this.name = name;
    }
}

class Student extends Person {

    public String number;

    public Student(String name, String number) {
        super(name);
        this.number = number;
    }
}

 

7.10 추상 클래스

  • 추상: 실체 간에 공통되는 특성을 추출한 것
  • 실체 클래스: 객체를 생성할 수 있는 클래스

 

추상 클래스

  • 추상 클래스: 실체 클래스의 공통적인 필드나 메소드를 추출해서 선언한 클래스로 실체 클래스의 부모 역할
  • 추상 클래스는 new연산자를 사용해서 객체를 직접 생성할 수 없음
  • 부모 클래스로만 사용 = extends 뒤에만 올 수 있음 = 상속을 통해 자식 클래스만 만들 수 있음
  • abstract 키워드 사용
  • 필드, 메소드, 생성자 선언 가능

 

추상 메소드

  • 메소드 선언부(리턴 타입, 메소드명, 매개변수)만 동일하고 실행 내용은 자식 클래스마다 달라야 하는 경우 있음
  • 이런 경우를 위해서 추상 클래스는 추상 메소드 선언 가능
  • abstract 키워드 사용하고 {} 가 없음 = 실행 내용은 없음 -> 따라서 자식 클래스에서 반드시 오버라이딩해서 실행 내용을 채워야 한다.
public class AbstractDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.breathe();
        dog.sound();

        Cat cat = new Cat();
        cat.breathe();
        cat.sound();
    }
}

// 추상 클래스
abstract class Animal {
    String name;

    public void breathe(){
        System.out.println("숨을 쉽니다");
    }

    // 추상 메소드로 animal의 공통 메소드
    abstract void sound();
}

class Dog extends Animal {
    // 실제로 각각의 객체에서 어떻게 사용할지는 오버라이딩을 통해 정해진다
    @Override
    void sound() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("냥냐");
    }
}

 

7.11 봉인된 클래스

  • final 클래스를 제외한 모든 클래스는 부모 클래스가 될 수 있음
  • 무분별한 자식 클래스 생성 방지를 위해 sealed 클래스 도입 
public class SealedDemo {
}

// Person2는 Employee와 Manager에만 상속할 수 있음 = 다른 클래스에는 상속 불가
sealed class Person2 permits Employee, Manager {
    
}

// student클래스는 상속을 받을 수 없음
// class Student extends Person2 {}


// 더 이상 상속 할 수없음
final class Employee extends Person2{
    
} 

// 봉인을 해제하고 상속할 수 있음
non-sealed class Manager extends Person2 {
    
}

class Director extends Manager {
    
}

 

최근에 올라온 글