Programming Language/Java

[Java] 07. OOP - 상속과 접근제어자

lumana 2024. 3. 12. 22:19

상속과 접근제어자

상속(inheritance)

  • 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것
  • class 이름 뒤에 상속받고자 하는 클래스의 이름을 키워드 'extends'와 함께 써 주기만 하면 된다
class Child extends Parent {
    // ...
}
  • 조상 클래스 : 상속해주는 클래스 (a.k.a 부모, 상위(super), 기반(base) 클래스)
  • 자손 클래스 : 상속 받는 클래스 (a.k.a 자식, 하위(sub), 파생된(derived) 클래스)
  • 생성자와 초기화 블럭은 상속되지 않음. 멤버만 상속됨
  • 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다
class Tv {
	boolean power; // 전원상태(on/off)
	int channel;	// 채널

	void power()       {   power = !power; }
	void channelUp()   {   ++channel;      }
	void channelDown() {   --channel;      }
}


class CaptionTv extends Tv {
    boolean caption;    // 캡션상태(on/off)
    void displayCaption(String text) {
        if (caption) {    // 캡션상태가 on(true)일 때만 text를 보여줌
            System.out.println(text);
        }
    }
}

class CaptionTvTest {
    public static void main(String args[]) {
        CaptionTv ctv = new CaptionTv();
        ctv.channel = 10;
        ctv.channelUp();
        System.out.println(ctv.channel);
        ctv.displayCaption("Hello, World");
        ctv.caption = true;    // 캡션(자막) 기능 활성화
        ctv.displayCaption("Hello, World");
    }
}

클래스간의 관계 - 포함관계

  • 포함관계를 맺어주면 상속하지 않아도 클래스를 재사용할 수 있다
class Car {
    Engine e = new Engine(); // 엔진
    Door[] d = new Door[4]; // 문, 문의 개수를 넷으로 가정하고 배열 처리
    // ...
}

클래스간의 관계 결정하기

  • 원은 점이다(is a) vs 원은 점을 가지고 있다(has a)
  • 상속관계 : is-a
  • 포함관계 : has-a

단일 상속

  • c++은 다중상속이 허용되지만 자바에서는 단일 상속만을 허용한다 (다이아몬드 관계)
  • 하나 이상의 클래스로부터 상속을 받을 수 없다
  • 다중상속이 필요할 것 같으면 포함관계를 이용해보자

Object 클래스 - 모든 클래스의 조상

  • Object 클래스는 모든 클래스 상속 계층도의 최상위에 있는 조상클래스임 (아무것도 extends 안하면 자동으로 Object 클래스를 extends 함)

오버라이딩

  • 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버랑이딩이라고 함

오버라이딩의 조건

  • 자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와
    • 이름이 같아야 한다
    • 매개변수가 같아야 한다
    • 반환 타입이 같아야 한다
  • 부모보다 접근 제어자가 같거나 커야한다
  • 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
  • 인스턴스 메서드를 static 메서드로 또는 그 반대로 변경할 수 없다.
    • static메서드의 경우 각 클래스에서 별개의 static 메서드를 정의한 것이지 오버라이딩이 아님

super

  • super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
  • this()와 마찬가지로 super() 역시 생성자이다.
  • super()는 조상클래스의 생성자를 호출하는데 사용됨
  • Object 클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 함
    • 그렇지 않으면 컴파일러는 생성자의 첫 줄에 super();를 자동적으로 추가함
  • example
class PointTest2 {
    public static void main(String args[]) {
        Point3D p3 = new Point3D();
        System.out.println("p3.x = " + p3.x);
        System.out.println("p3.y = " + p3.y);
        System.out.println("p3.z = " + p3.z);
    }
}

class Point {
    int x = 10;
    int y = 20;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point {
    int z = 30;

    Point3D() {
        this(100, 200, 300);    // Point3D(int x, int y, int z) 호출
    }

    Point3D(int x, int y, int z) {
        super(x, y);            // Point(int x, int y) 호출
        this.z = z;
    }
}

패키지

  • 패키지란 클래스의 묶음임
  • 패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며, 관련 클래스끼리 그룹 단위로 묶어 놓음으로써 클래스를 효율적으로 관리한다
  • 클래스가 물리적으로 하나의 클래스파일(.class) 인 것과 같이 패키지는 물리적으로 하나의 디렉토리임
  1. 하나의 소스파일에는 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용함
  2. 모든 클래스는 반드시 하나의 패키지에 속해야 한다
  3. 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
  4. 패키지는 물리적으로 클래스 파일(.class)를 포함하는 하나의 디렉토리이다.

패키지의 선언

package 패키지명;
  • 소스파일에서 주석과 공백을 제외한 첫 번째 문장이어야 하고, 단 한번만 선언될 수 있다.
  • 대소문자를 모두 허용(소문자 원칙)
  • 자바에서 패키지를 따로 선언하지 않으면 이름없는 패키지에 속하게 됨
  • '-d' 옵션을 추가하여 컴파일 한다
package com.javachobo.book;

class PackageTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
C:\jdk1.8\work>javac -d . PackageTest.java

import문

  • 다른 패키지의 클래스를 사용하려면 패키지 명이 포함된 클래스 이름을 사용해야 한다
  • 매 번 이름을 사용하기 불편함 --> import문을 통해 미리 명시해주자
import 패키지명.클래스명;
or
import 패키지명.*;
  • '*'를 사용하는 것이 하위 패키지의 클래스까지 포함하는 것은 아니다

static import문

  • static import문을 사용하면 static 멤버를 호출할 때 클래스 이름을 생략할 수 있다.
import static java.lang.System.out;
import static java.lang.Math.*;

class StaticImportEx1 {
    public static void main(String[] args) {
        // System.out.println(Math.random());
        out.println(random());

        // System.out.println("Math.PI : " + Math.PI);
        out.println("Math.PI : " + PI);
    }
}

제어자

  • 접근 제어자 - public, protected, default, private
    • 접근 제어자는 4가지 중 하나만 선택해서 사용할 수 있음
  • 그 외 - static, final, abstract, .....

static

  • '클래스의', '공통적인'의 의미를 가짐

final

  • '마지막의' or '변경될 수 없는'의 의미를 가짐
  • 변수에 사용하면 값을 변경할 수 없는 상수가 됨
  • 메서드에 사용되면 오버라이딩을 할 수 없게 됨
  • 클래스에 사용되면 자신을 확장하는 자손 클래스를 정의하지 못하게 된다.
  • final이 붙은 인스턴스 변수의 경우 생성자에서 초기화 되도록 할 수 있음
    • 클래스 내에 매개변수를 갖는 생성자를 선언하여, 인스턴스를 생성할 때 fianl 이 붙은 멤버변수를 초기화한다
    • 인스턴스마다 final이 붙은 멤버변수가 다른 값을 가질 수 있게 됨
class Card {
    final int NUMBER;            // 상수지만 선언과 함께 초기화하지 않고
    final String KIND;            // 생성자에서 단 한번만 초기화할 수 있음
    static int width    = 100;
    static int height    = 250;

    Card(String kind, int num) {
        KIND = kind;
        NUMBER = num;
    }

    Card() {
        this("HEART", 1);
    }

    public String toString() {
        return KIND + " " + NUMBER;
    }
}

class FinalCardTest {
    public static void main(String args[]) {
        Card c = new Card("HEART", 10);
//        c.NUMBER = 5;
        System.out.println(c.KIND);
        System.out.println(c.NUMBER);
        System.out.println(c);            // System.out.println(c.toString());
    }
}

abstract

  • '미완성'의 의미를 가지고 있음
  • 메서드의 선언부만 작성하고 실제 수행내용은 구현되지 않은 추상 메서드를 선언하는데 사용됨
  • 클래스에 abstract -> 클래스 내에 추상 메서드가 선언되어 있음을 의미함
  • 인스턴스를 생성하지 못하게 하는 목적으로, 완성된 클래스에 abstract를 붙어 추상클래스로 만드는 경우도 있다

접근 제어자(access modifier)

  • 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다
  • 따로 지정되어 있지 않으면 default임
클래스, 메머변수, 메서드, 생성자에서 접근 제어자를 사용할 수 있음
private - 같은 클래스 내에서만 접근이 가능하다
default - 같은 패키지 내에서만 접근이 가능하다
protected - 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능하다
public - 접근 제한이 전혀 없다

public > protected > (default) > private
  • 접근 제어자를 통해 클래스 내부에 선언된 데이터를 보호할 수 있음
    • 객체지향의 캡슐화에 해당하는 내용
  • 임시로 사용되는 것들을 숨길 수도 있음
  • 접근 제어자의 범위에 따라 테스트 하는 범위가 달라진다
  • 생성자에 접근 제어자를 사용하여 인스턴스의 생성을 제한할 수도 있다
    • 생성자가 private -> 외부에서 인스턴스 생성 제한
    • 인스턴스를 생성해서 반환해주는 public메서드를 제공함으로써 외부에서 이 클래스의 인스턴스를 사용할 수 있도록 해야함
    • 이 클래스는 private static 이어야 함
    • 생성자가 private인 경우 다른 클래스의 조상이 될 수 없음
      • 이런 경우 클래스에 final을 추가해 상속할 수 없는 클래스 라는 것을 밝혀주자
final class Singleton {
    private static Singleton s = new Singleton();

    private Singleton() {
        // ...
    }

    public static Singleton getInstance() {
        if (s==null)
            s = new Singleton();
        return s;
    }
}

class SingletonTest {
    public static void main(String args[]) {
//        Singleton s = new Singleton();
        Singleton s = Singleton.getInstance();
    }
}

제어자 조합 시 주의사항

  1. 메서드에 static과 abstract를 함께 사용할 수 없음
  • static 메서드는 몸통이 있는 메서드에만 사용할 수 있음
  1. 클래스에 abstract와 final을 동시에 사용할 수 없다
  • 서로 모순됨(상속으로 구현 vs 상속 불가)
  1. abstract 메서드의 접근 제어자가 private 일 수 없다
  • 자손 클래스에서 구현해야 하는데 private??
  1. 매서드에 private와 final을 같이 사용할 필요는 없다
  • 접근 제어자가 private인 메서드는 오버라이딩 될 수 없기 때문이다. 둘 중 하나만 써도 충분함

'Programming Language > Java' 카테고리의 다른 글

[Java] 09. OOP - 추상 클래스(abstract class)  (0) 2024.06.24
[Java] 08. OOP - 다형성(polymorphism)  (0) 2024.06.24
[Java] 06. OOP - 클래스와 객체  (0) 2024.03.12
[Java] 05. 배열  (0) 2024.03.12
[Java] 04. 변수  (0) 2024.03.12