코드스테이츠 백엔드 부트캠프 11일차 [객체지향 프로그래밍 심화]

wish17·2022년 12월 29일
0
post-thumbnail

Section1 - [Java] 객체지향 프로그래밍 심화

상속(Inheritance)

기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소를 의미

  • 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속받게 된다.
    따라서 하위 클래스의 멤버 개수는 언제나 상위 클래스의 그것과 비교했을 때 같거나 많다.

  • 단일 상속(single inheritance)만을 허용한다.
    (아버지는 한명 뿐... 물론 그 위로 아버지 위에 할아버지, 증조할아버지는 가능)

코드 예제

class Person {
    String name;
    int age;

    void learn(){
        System.out.println("공부를 합니다.");
    };
    void walk(){
        System.out.println("걷습니다.");
    };
    void eat(){
        System.out.println("밥을 먹습니다.");
    };
}

class Programmer extends Person { // Person 클래스로부터 상속. extends 키워드 사용 
    String companyName;

    void coding(){
        System.out.println("코딩을 합니다.");
    };
}

class Dancer extends Person { // Person 클래스로부터 상속
    String groupName;

    void dancing(){
		    System.out.println("춤을 춥니다.");
		};
}

class Singer extends Person { // Person 클래스로부터 상속
    String bandName;

    void singing(){
		    System.out.println("노래합니다.");
		};
    void playGuitar(){
		    System.out.println("기타를 칩니다.");
		};
}

public class HelloJava {
    public static void main(String[] args){

        //Person 객체 생성
        Person p = new Person();
        p.name = "김코딩";
        p.age = 24;
        p.learn();
        p.eat();
        p.walk();
        System.out.println(p.name);

        //Programmer 객체 생성
        Programmer pg = new Programmer();
        pg.name = "박해커";
        pg.age = 26;
        pg.learn(); // Persons 클래스에서 상속받아 사용 가능
        pg.coding(); // Programmer의 개별 기능
        System.out.println(pg.name);

    }
}

//출력값
공부를 합니다.
밥을 먹습니다.
걷습니다.
김코딩
공부를 합니다.
코딩을 합니다.
박해커

포함 관계

포함(composite)
= 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것

public class Employee {
    int id;
    String name;
    Address address;

    public Employee(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    void showInfo() {
        System.out.println(id + " " + name);
        System.out.println(address.city+ " " + address.country);
    }

    public static void main(String[] args) {
        Address address1 = new Address("서울", "한국");
        Address address2 = new Address("도쿄", "일본");

        Employee e = new Employee(1, "김코딩", address1);
        Employee e2 = new Employee(2, "박해커", address2);

        e.showInfo();
        e2.showInfo();
    }
}

class Address {
    String city, country;

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }
}

// 출력값
1 김코딩
서울 한국
2 박해커
도쿄 일본

Address 클래스로 인스턴스 변수 city와 country를 묶어 Employee 클래스 안에 참조변수를 선언하는 방법으로 코드의 중복을 없애고 포함관계로 재사용하고 있다.

상속or포함 어떤 걸 써야할지 결정하는 방법

  • 상속 = 클래스 간의 관계가 ‘~은 ~이다(IS-A)’ 관계
  • 포함 = ~은 ~을 가지고 있다(HAS-A) 관계

상속 관계에서의 메서드 오버라이딩

상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것을 의미

class Vehicle {
    void run() {
        System.out.println("Vehicle is running");
    }
}

public class Bike extends Vehicle { // Vehicle 클래스 상속
    void run() {
        System.out.println("Bike is running"); // 메서드 오버라이딩
    }

    public static void main(String[] args) {
        Bike bike = new Bike();
        bike.run();
    }
}

// 출력값
"Bike is running"

메서드 오버라이딩을 사용하기 위한 조건

    1. 메서드의 선언부(메서드 이름, 매개변수, 반환타입)이 상위클래스의 그것과 완전히 일치해야한다.
    1. 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다.
    1. 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다.

메서드 오버라이딩의 사용성 (장점)

public class Main {
    public static void main(String[] args) {
        Bike bike = new Bike(); // 각각의 타입으로 선언 + 각각의 타입으로 객체 생성
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        
	    bike.run();
        car.run();
        motorBike.run();

	    Vehicle bike2 = new Bike(); // 상위 클래스 타입으로 선언 + 각각 타입으로 객체 생성
        Vehicle car2 = new Car();
        Vehicle motorBike2 = new MotorBike();

        bike2.run();
        car2.run();
        motorBike2.run();
    }
}

class Vehicle {
    void run() {
        System.out.println("Vehicle is running");
    }
}

class Bike extends Vehicle {
    void run() {
        System.out.println("Bike is running");
    }
}

class Car extends Vehicle {
    void run() {
        System.out.println("Car is running");
    }
}

class MotorBike extends Vehicle {
    void run() {
        System.out.println("MotorBike is running");
    }
}

// 출력값
Bike is running
Car is running
MotorBike is running
Bike is running
Car is running
MotorBike is running

상위 클래스 타입으로 선언하건 각각의 타입으로 선언하건 동일한 출력이 나오는 것을 확인할 수 있다.

참조 변수 bike2, car2, motorBike2는 모두 Vehicle 타입이지만 메서드 오버라이딩을 통해 각각의 run() 메서드가 출력되고 이를 이용해 간편하게 배열로 선언하여 관리할 수 있는 편리성이 있다.

// 배열로 한번에 관리하기

Vehicle[] vehicles = new Vehicle[] { new Bike(), new Car(), new MotorBike()};
for (Vehicle vehicle : vehicles) {
		vehicle.run();
}

// 출력값
Bike is running
Car is running
MotorBike is running

super 키워드와 super()

super 키워드는 상위 클래스의 객체, super()는 상위 클래스의 생성자를 호출하는 것을 의미

리마인드
 this는 자신의 객체, this() 메서드는 자신의 생성자 호출을 의미

this,this(),super,super() 모두 공통적으로 상위 클래스의 존재를 상정하며 상속 관계를 전제로 한다.

super 키워드

아래와 같이 하위클래스가 상위클래스에서 변수를 상속받을 때 변수명이 같아 구분이 필요할 때 쓰는게 super키워드다.

public class Example {
    public static void main(String[] args) {
        SubClass subClassInstance = new SubClass();
        subClassInstance.callNum();
    }
}

class SuperClass {
    int count = 20; // super.count
}

class SubClass extends SuperClass {
    int count = 15; // this.count

    void callNum() {
        System.out.println("count = " + count);
        System.out.println("this.count = " + this.count);
        System.out.println("super.count = " + super.count);
    }
}


// 출력값
count = 15
count = 15
count = 20
  • 만약 super 키워드를 붙이지 않는다면, 자바 컴파일러는 해당 객체는 자신이 속한 인스턴스 객체의 멤버를 먼저 참조함.

  • this와 super는 기본적으로 같은 것이라 말할 수 있지만 상위 클래스의 멤버와 자신의 멤버를 구별하는 데 사용된다는 점이 다르다.

super() 메서드

상위 클래스의 생성자를 호출하는데 사용

this()는 같은 클래스의 다른 생성자를 호출하는데 사용

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
    }
}

class Human {
    Human() { //Human class 생성자
        System.out.println("휴먼 클래스 생성자");
    }
}

class Student extends Human { // Human 클래스로부터 상속
    Student() {    
        super(); // Human 클래스의 생성자 호출
        System.out.println("학생 클래스 생성자");
    }
}

// 출력값
휴먼 클래스 생성자
학생 클래스 생성자
  • super() 메서드는 생성자 안에서만 사용가능하고, 반드시 첫 줄에 와야한다.
  • 모든 생성자의 첫 줄에는 반드시 this() 또는 super()가 선언되어야 한다.
    (만약 super()가 없는 경우에는 컴파일러가 생성자의 첫 줄에 자동으로 super()를 삽입한다. 이때 상위클래스에 기본생성자가 없으면 에러가 발생)

클래스의 정점, Object 클래스

상속계층도에서 최상위에 위치한 상위클래스

  • 자바의 모든 클래스는 Object 클래스로부터 확장된다.
    (아무런 상속을 받지 않은 클래스는 컴파일러가 자동으로 Object에 상속 시킴)

  • Object 클래스의 멤버들을 자동으로 상속받아 사용할 수 있음.

Object 클래스의 대표적인 메서드


캡슐화(Encapsulation)

특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것

캡슐화를 해야하는 이유

  • 데이터 보호 (정보 은닉(data hiding)을 통한 오류 범위 최소화)
  • 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지

패키지

특정한 목적을 공유하는 클래스와 인터페이스의 묶음

  • 클래스들을 그룹 단위로 묶어 효과적으로 관리
  • 클래스의 충돌을 방지

Import문

다른 패키지 내의 클래스를 사용하기 위해 사용
(패키지 구문과 클래스문 사이에 작성)

cf) import문은 컴파일 시에 처리가되므로 프로그램의 성능에는 영향을 주지 않음

import 패키지명.클래스명; 또는 import 패키지명.*;

ex)
import practicepack.test.ExampleImp

접근 제어자

제어자(Modifier)

클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 의미한다.

제어자는 크게 접근 제어자와 기타 제어자로 구분한다.

접근 제어자public, protected, (default), private
기타 제어자static, final, abstract, native, transient, synchronized 등

하나의 대상에 대해서 여러 제어자 사용 가능하지만 접근 제어자는 단 한번만 사용할 수 있다.

접근 제어자(Access Modifier)

접근 제어자를 사용하면 클래스 외부로의 불필요한 데이터 노출을 방지(data hiding)할 수 있고, 외부로부터 데이터가 임의로 변경되지 않도록 막을 수 있다.

접근 제어자접근 제한 범위
private동일 클래스에서만 접근 가능
default동일 패키지 내에서만 접근 가능
protected동일 패키지 + 다른 패키지의 하위 클래스에서 접근 가능
public접근 제한 없음

(cf. default = 아무런 접근 제어자를 붙이지 않는 경우 기본적인 설정)

접근 제한 범위

private(동일 클래스) < default(동일 패키지) < protected(동일 패키지 + 하위클래스) < public(접근 제한 없음)


getter와 setter 메서드

접근 제어자로 은닉 된 객체의 변수의 데이터 값을 추가하거나 수정하는 방법

  • setter 메서드

    • 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 가능하게 해주는 메서드
  • getter 메서드

    • setter 메서드로 설정한 변수 값을 읽어오는 데 사용하는 메서드

오늘의 정리

상속

상위 클래스-하위 클래스의 상속 관계,장점

  • 관계
    • 상위 클래스의 멤버(필드, 메서드, 이너 클래스)를 하위 클래스와 공유하는 것
  • 장점
    • 코드의 중복을 제거, 다형적 표현이 가능
      (다형성 = 하나의 객체가 여러 모양으로 표현될 수 있다는 것)

포함관계와 상속관계의 차이

  • 상속관계
    • 다른 클래스의 모든 멤버를 상속 받은 관계
  • 포함관계
    • 다른 클래스의 일부 멤버를 선언해 일부가 포함된 관계

상속 관계에서 사용할 수 있는 메서드 오버라이딩의 정의, 성립 조건, 장점을 이해하고 이를 활용할 수 있다.

  • 정의
    • 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것
  • 조건
    • 동일한 선언부(메서드 이름, 매개변수, 반환타입),
    • 접근 제어자의 범위가 상위 메서드보다 같거나 넓어야 함
    • 예외는 상위 클래스의 메서드보다 많이 선언할 수 없음
  • 장점
    • 모든 객체를 상위 클래스 타입 하나로 간편하게 한번에 관리할 수 있다.
      (상위 클래스 타입으로 선언하건 각각의 타입으로 선언하건 동일한 출력이 나오기 때문)

super 와 super() 의 차이

super 키워드는 상위 클래스의 객체, super()는 상위 클래스의 생성자를 호출


캡슐화

캡슐화의 핵심 개념과 목적

특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것

패키지의 개념

클래스와 인터페이스의 묶음

import문 사용법

import 패키지명.클래스명를 이용해 사용
(import를 안써도 패키지명을 붙여서 클래스 멤버를 사용할 수 있지만 매우 번거로움)

접근제어자 네 가지 각각의 접근 가능 범위

private(동일 클래스) < default(동일 패키지) < protected(동일 패키지 + 하위클래스) < public(접근 제한 없음)


질문

Q: 상속과 이너클래스의 사용성 측면에서 차이점이 궁금합니다!
메서드 오버라이딩을 제외하고는 멤버를 가져다 쓸 수 있는 구조가 유사해 보여 기능상 차이점이 눈에 띄지 않는데 대표적인 차이점들이 있을까요??

A: 이너클래스, 익명클래스는 1회성으로 잠깐 사용하고 말 객체를 쓰는 거고,
상속은 공통기능을 재활용 하기 위함이쥬

0개의 댓글