[코드스테이츠 백엔드 44기 SEB BE] 11일차

오태호·2023년 2월 27일
0

코드스테이츠

목록 보기
11/22
post-thumbnail

상속

상속

  • 기존 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소
  • 두 클래스를 상위 클래스와 하위 클래스로 나누어 상위 클래스의 멤버를 하위 클래스와 공유하는 것을 의미
    • 위 두 클래스를 서로 상속 관계에 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속받게 됨
  • 하위 클래스는 상위 클래스의 멤버를 상속받기 때문에 하위 클래스의 멤버 개수는 항상 상위 클래스의 멤버 개수보다 같거나 많음
  • "~클래스로부터 확장되었다" -> 두 클래스 간 상속 관계를 설정할 때 사용하는 extends 키워드 자체가 확장한다는 의미를 갖고 있음
  • 자바의 객체지향 프로그래밍에서는 단일 상속만을 허용함
    • 다중 상속은 허용되지 않음
    • 인터페이스를 통해 다중 상속과 비슷한 효과를 낼 수 있음

상속의 사용 이유

  • 상속을 통해 클래스를 작성하면 코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있어 코드의 중복을 제거할 수 있다!
  • 다형적 표현이 가능하다!
    • 다형성
      • 하나의 객체가 여러 모양으로 표현될 수 있는 성질

포함 관계

  • 포함(composite)
    • 상속처럼 클래스를 재사용할 수 있는 방법
    • 클래스의 멤버로 다른 클래스 타입의 참조 변수를 선언하는 것
public class Person {
    String name;
    int age;
    Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String toString() {
        return String.format("이름 : %s\n나이 : %d\n주소 : %s", name, age, address.toString());
    }

    class Address {
        String country, city, street;

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

        public String toString() {
            return country + " " + city + " " + street;
        }
    }
}
  • Address 클래스로 변수들을 묶어준 후에 Person 클래스에 참조변수를 선언하여 코드의 중복을 없애고 포함관계로 재사용하고 있다!

포함 관계 vs 상속

  • 상속
    • 클래스 간의 관계가 '~은 ~이다(IS-A)' 관계
    • Ex. 고양이는 동물이다
  • 포함 관계
    • 클래스 간의 관계가 '~은 ~을 갖고 있다(HAS-A)' 관계
    • Ex. 사람은 주소를 갖고 있다

메서드 오버라이딩

메서드 오버라이딩(Overriding)

  • 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의
    • 상위 클래스에 정의된 메서드를 하위 클래스에서 자신의 동작에 맞게 변경하고자 할 때 사용
class Animal {
    void howl() {
        System.out.println("Howl...");
    }
}

class Dog extends Animal {
    void howl() {
        System.out.println("멍멍");
    }
}

class Wolf extends Animal {
    void howl() {
        System.out.println("아우우~");
    }
}
  • Animal 클래스의 howl 메서드를 Animal 클래스를 상속받은 Dog, Wolf 클래스 각각에서 오버라이딩 하는 예시
    • 만약 Dog나 Wolf의 인스턴스를 통해 howl 메서드를 호출하면 Animal의 howl 메서드가 아닌 Dog나 Wolf의 howl 메서드가 호출된다

메서드 오버라이딩의 조건

  1. 메서드 선언부가 상위 클래스의 것과 같아야 한다
  2. 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다
  3. 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다
    1. 상위 클래스의 메서드에서 throws를 선언했더라도 하위 클래스는 throws를 처리하지 않을 수 있다
    2. 하위 클래스는 상위 클래스의 메서드에서 throws하는 예외와 같은 예외를 throws할 수 있다
      • 1, 2번 규칙을 통해 하위 클래스에서는 상위 클래스보다 상위 예외를 throws 할 수 없음을 알 수 있다
    3. 하위 클래스는 상위 클래스의 메서드에서 throws하는 예외의 하위 예외만 throws할 수 있다
    4. 하위 클래스는 Runtime 예외를 상위 클래스의 메서드와 상관없이 throws할 수 있다

메서드 오버라이딩 장점

  • 같은 기능을 구현한 하위 클래스에서 동일한 메서드를 사용함으로써 코드의 일관성을 제공할 수 있다

super 및 super()

  • 공통적으로 모두 상위 클래스가 존재함을 가정하고 상속 관계를 전제로 한다

super

  • 상위 클래스의 객체를 의미
class Laptop {
    String model = "노트북";
    String brand = "브랜드";
}

class GalaxyBook extends Laptop {
    String model = "갤럭시북";
    boolean calledBixby;

    GalaxyBook() {
        super.model = model;
        super.brand = "삼성";
    }
}

class MacBook extends Laptop {
    String model = "맥북";
    boolean calledSiri;

    MacBook() {
        super.model = model;
        super.brand = "애플";
    }
}
  • 상위 클래스의 변수를 참조해야하는 경우가 존재하는데 그 경우 super 키워드를 사용하여 부모의 객체의 멤버값을 참고할 수 있다
    • 위 예시처럼 상위 클래스와 하위 클래스에 같은 이름의 변수가 존재할 때, super 키워드를 통해 구분할 수 있다
      • 만약 super 키워드를 붙여주지 않는다면, 컴파일러는 자신이 속한 인스턴스 객체의 멤버를 먼저 참조한다

super()

  • 상위 클래스의 생성자를 호출
class Laptop {
    String model = "노트북";
    String brand = "브랜드";

    public Laptop() {
        System.out.println("Laptop 생성자");
    }
}

class GalaxyBook extends Laptop {
    String model = "갤럭시북";
    boolean calledBixby;

    GalaxyBook() {
        super();
        System.out.println("GalaxyBook 생성자");
        super.model = model;
        super.brand = "삼성";
    }
}

class MacBook extends Laptop {
    String model = "맥북";
    boolean calledSiri;

    MacBook() {
        super();
        System.out.println("MacBook 생성자");
        super.model = model;
        super.brand = "애플";
    }
}
  • Laptop 클래스를 확장하여 GalaxyBook, MacBook 클래스를 생성하고 GalaxyBook, MacBook 생성자를 통해 상위 클래스의 생성자를 super()를 통해 호출
    • super() 메서드는 생성자 안에서만 사용 가능하고 반드시 첫 줄에 위치해야 한다!
  • 모든 생성자의 첫 줄에는 반드시 this() 또는 super()가 선언되어야 한다!
    • 만약 super()가 없다면 컴파일러가 자동으로 생성자의 첫 줄에 super()를 삽입
    • 상위 클래스에 기본 생성자가 없으면 에러가 발생한다
    • 왜 생성자의 첫 줄에 존재해야 하는가?
      • 하위 클래스의 멤버가 상위 클래스의 멤버를 사용할 수도 있는데, 이 때 조상의 멤버들이 먼저 초기화되어있지 않으면 안되기 때문!
  • 상위 클래스 생성자의 호출은 클래스의 상속관계를 거슬러 올라가며 반복된다
    • 모든 클래스의 최고 조상인 Object 클래스의 생성자인 Object()까지 가서야 끝난다

      Object 클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 한다! 만약 개발자가 추가하지 않는다면 컴파일러가 자동으로 첫 줄에 super()를 추가한다!


Object 클래스

  • 자바의 클래스 상속계층도에서 최상위에 위치한 최상위 클래스
  • 자바의 모든 클래스는 Object 클래스로부터 확장된다
    • 컴파일러는 컴파일 과정에서 다른 클래스로부터 상속 받지 않는 클래스에 자동으로 extends Object를 추가하여 Object 클래스를 상속받도록 한다

캡슐화

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

캡슐화를 하는 이유

  1. 데이터 보호의 목적
  2. 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지

캡슐화의 장점

  • 정보 은닉(data hiding)
    • 외부로부터 객체의 속성 및 기능이 함부로 변경되지 못하게 막는다
    • 데이터가 변경되더라도 다른 객체에 영향을 주지 않기에 독립성을 확보한다
    • 유지보수코드 확장 시에 오류의 범위를 최소화할 수 있어 코드 유지보수에 용이하다

패키지(Package)

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

패키지의 장점

  • 클래스를 그룹 단위로 묶어 효과적으로 관리
    • 패키지는 물리적인 하나의 디렉토리
    • 디렉토리는 하나의 계층구조를 가지는데, 계층 구조 간 구분은 점(.)을 통해 표현
  • 클래스의 충돌을 방지
    • 같은 이름의 클래스를 가지더라도 다른 패키지에 존재한다면 이름명으로 인한 충돌을 방지할 수 있다
  • 패키지가 있다면 소스 코드의 첫 번째 줄에 package 패키지명 으로 패키지를 표시

import문

  • 다른 패키지 내의 클래스를 사용하기 위해 작성
import 패키지명.클래스명; 또는 import 패키지명.*;
  • 같은 패키지에서 여러 클래스가 사용될 때는 import 패키지명.*로 작성하여 해당 패키지의 모든 클래스를 패키지명 없이 사용
  • import문은 컴파일 시에 처리되므로 프로그램 성능에 영향을 주지 않는다
package package1;

public class Address {
    public String country, city, street;

    public void print() {
        System.out.println(country + " " + city + " " + street);
    }
}
package package2;

public class Person {
    public static void main(String[] args) {
        package1.Address address = new package1.Address();
    }
}
  • 만약 다른 패키지에 존재하는 클래스를 사용할 때, import문을 사용하지 않는다면 다른 패키지에 존재하는 클래스를 사용할 때 매번 패키지명을 붙여야만 한다
    • import문을 통해 컴파일러에게 사전에 소스파일에 사용된 클래스에 대한 정보를 제공

접근 제어자

제어자(Modifier)

  • 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드
  • 접근 제어자기타 제어자로 구분할 수 있다
    • 접근 제어자
      • public, protected, default, private
    • 기타 제어자
      • static, abstract, final, native, transient, synchronized 등
  • 하나의 대상에 여러 제어자를 사용할 수 있다
    • 각 대상에 대해 접근 제어자는 한 번만 사용할 수 있다

접근 제어자(Access Modifier)

  • 캡슐화를 구현하기 위한 핵심적인 방법
  • 접근 제어자를 통해 클래스 외부로의 불필요한 데이터 노출을 방지하고 외부로부터 임의로 변경되지 않도록 막을 수 있다
    • 데이터 보호 측면에서 중요하다!
접근 제어자접근 제한 범위
private동일 클래스에서만 접근 가능
default동일 패키지 내에서만 접근 가능
protected동일 패키지 및 다른 패키지의 하위 클래스에서 접근 가능
public접근 제한 없음
  • 접근 제한 범위
    • public > protected > default > private
  • default
    • 아무런 접근 제어자를 붙이지 않은 경우 기본적인 설정을 의미

getter와 setter

getter와 setter

  • 캡슐화 목적 달성을 위해 private과 같은 접근 제어자를 이용하기도 하는데 이럴 경우 데이터 변경을 할 수 없는 경우가 생김
  • 이러한 경우 getter와 setter 메서드를 사용
public class Person {
    private String name;
    private int age;
    private String residentRegistrationNumber;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getResidentRegistrationNumber() {
        return residentRegistrationNumber;
    }

    public void setResidentRegistrationNumber(String residentRegistrationNumber) {
        this.residentRegistrationNumber = residentRegistrationNumber;
    }
}
  • setter 메서드
    • 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 가능하게 해준다
    • 일반적으로 메서드명 앞에 set-을 붙여 정의한다
  • getter 메서드
    • 설정한 변수 값을 읽어오는데 사용하는 메서드
    • 객체 외부에서 필드값을 사용하기에 부적절한 경우가 있는데, 이런 경우 그 값을 가공해서 외부로 전달하는 역할을 하기도 한다
    • 일반적으로 메서드명 앞에 get-을 붙여 정의한다

getter와 setter 메서드 사용 이유

  • 데이터를 효과적으로 보호하면서 의도하는 값으로 값을 변경하여 캡슐화를 보다 효과적으로 달성할 수 있다
profile
자바, 웹 개발을 열심히 공부하고 있습니다!

0개의 댓글