Language/Java

[Java] 객체지향 프로그래밍2 (상속, 오버라이딩, package, 제어자)

Ella_K 2022. 12. 24. 06:18

상속

정의

기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것

- 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성된다.
- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

 

장점

보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.

Ex.

class Parent {}
class Child extends Parent {}
class Child2 extends Parent {}

Child, Child2 클래스에 공통적으로 추가되어야 하는 멤버가 있다면, 두 클래스에 각각 따로 추가해주는 것보다 공통 조상인 Parent 클래스에 추가하는 것이 좋다.

클래스 간의 상속관계를 맺어 주면 자손 클래스들의 공통적인 부분은 조상 클래스에서 관리하고 자손 클래슨는 자신에 정의된 멤버들만 관리하면 되므로 코드 중복이 줄어들고, 각 클래스의 코드가 적어져서 관리가 쉬어진다.

 

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

클래스를 재사용하는 또 다른 방법 - 클래스간에 '포함'관계를 맺어 준다. → 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언한다.

Ex.

class Point {
    int x;
    int y;
}

class Circle {
    Point c = new Point();
    int r;
}
상속 관계 '~은 ~이다.(is-a)'
포함 관계 '~은 ~을 가지고 있다.(has-a)'

 

단일 상속

다른 객체 지향 언어인 C++에서는 여러 조상 클래스로부터 상속받는 것이 가능한 '다중 상속'을 허용한다.

하지만 자바에서는 오직 단일 상속만 허용한다.

다중 상속의 장점:
여러 클래스로부터 상속 받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있따.

다중 상속의 단점:
클래스간의 관계가 매우 복잡해진다.
서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다.

단일 상속의 장점:
클래스 간의 관계가 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다.

 

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

모든 클래스 상속계층도의 최상위에 있는 조상클래스

다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 함으로써 최상위 조상 클래스가 가능하다.

자바의 모든 클래스들은 Object 클래스의 멤버들을 상속 받기 때문에 Object 클래스에 정의된 멤버들을 사용할 수 있다. (ex. toString(), equals(Object o))


오버라이딩

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것

 

오버라이딩의 조건

오버라이딩은 메서드의 내용만 새로 작성하는 것으로 메서드의 선언부는 조상의 것과 완전히 일치해야 한다.

(자손 클래스에서 오버라이딩하는 메서드는 조상 클래스이 메서드와 이름, 매개변수, 반환타입이 같아야 한다.)

다만 접근 제어자와 예외는 제한된 조건 하에서만 다르게 변경할 수 있다.

1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

3. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

 

super

자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조 변수

조상 클래스로부터 상속받은 멤버도 자손 클래스 자신의 멤버이므로  super대신 this를 사용할 수 있다. 그래도 조상 클래스의 멤버와 자손클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우에만 super를 사용하는 것이 좋다.

Ex. 조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에 super를 사용한다.

class Point{
    int x;
    int y;

    String getLocation(){
        return "x:" + x + ", y:" + y;
    }
}

class Point3D extends Point{
    int z;
    String getLocation(){
        // return return "x:" + x + ", y:" + y + ", z:" + z;
        return super.getLocation() + ", z:" + z; // 조상의 메서드 호출
    }
}
후에 조상클래스의 메서드가 변경되더라도 변경된 내용이 자손클래스의 메서드에 자동적으로 반영될 것이다.
따라서 조상의 클래스의 메서드의 내용에 추가적으로 작업을 덧붙이는 경우라면 super를 사용해서 조상의 메서드를 포함시키는 것이 좋다. 

 

super() - 조상 클래스의 생성자

조상 클래스의 생성자를 호출한다.

Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에 자신의 다른 생성자, this(), 또는 조상의 생성자를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super();' 를 생성자의 첫줄에 추가한다.

자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 한다.
따라서 생성자의 첫 줄에 조상클래스의 생성자를 호출해야한다.
class Point{
    int x, y;

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

    String getLocation(){
        return "x:" + x + ", y:" + y;
    }
}

class Point3D extends Point{
    int z;

    Point3D() {
        this(100, 200, 300);
    }
    Point3D(int x, int y, int z){
        super(x, y);
        this.z = z;
    }
    String getLocation(){
        return super.getLocation() + ", z:" + z;
    }
}

package와 import

package

패키지란 클래스의 묶음이다.

같은 이름의 클래스 일지라도 서로 다른 패키지에 존재하는 것이 가능하므로, 자신만의 패키지 체계를 유지함으로써 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있다.

  • 하나의 소스파일에는 첫번째 문장으로 단 한 번의 패키지 선언만을 허용한다.
  • 모든 클래스는 반드시 하나의 패키지에 속해야 한다.
  • 패키지는 점을 구분자로 하여 계층구조로 구성할 수 있다.
  • 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리이다.

 

package 선언

package 패키지명;

패키지 선언문은 반드시 소스파일에서 주석과 공백을 제외한 첫 번째 문장이어야 하며, 한나의 소스파일에 단 한번만 선언될 수 있다.

패키지명은 클래스명과 쉽게 구분하기 위해서 소문자로 하는 것을 원칙으로 하고 있다.

 

import 문

클래스의 코드를 작성하기 전에 import문으로 사용하고자 하는 클래스의 패키지를 미리 명시해주면 소스코드에 사용되는 클래스이름에서 패키지명을 생략할 수 있다.

import문의 역할은 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하는 것이다.

import 패키지명.클래스명;
import 패키지명.*;
클래스명을 지정해주는 대신 '*'를 사용하면, 컴파일러는 해당 패키지에서 일치하는 클래스 이름을 찾아야 하는 수고를 더 해야하지만, 실행 시 성능상의 차이는 전혀 없다.

 

static import 문

static멤버를 호출할 때 클래스 이름을 생략할 수 있다.


제어자

제어자는 클래스, 변수, 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.

  • 접근 제어자: public, protected, default, private
  • 그 외: static, final, abstract, native, transient, synchronized, volatile, strictfp

하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능하다. 단, 접근 제어자는 한 번에 네 가지 중 하나만 선택해서 사용할 수 있다.

 

static - 클래스의, 공통적인

  • 사용되는 곳: 멤버변수, 메서드, 초기화 블럭
static 멤버변수 - 모든 인스턴스에 공통적으로 사용되는 클래스변수가 된다.
- 클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.
- 클래스가 메모리에 로드될 때 생성된다.
메서드 - 인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 된다.
- static메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다.

 

final - 마지막의. 변경될 수 없는

final 클래스 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
메서드 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.
지역변수

 

abstract - 추상의, 미완성의

abstract 클래스 클래스 내에 추상 메서드가 선언되어 있음을 의미한다.
메서드 선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알린다.

 

접근 제어자

멤버 또는 클래스에 사용되어, 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.

클래서, 메서드, 생성자에 접근 제어자를 지정되어 있지 않다면, 접근 제어가가 default임을 뜻한다.

제어자 같은 클래스 같은 패키지 자손클래스 전체
public O O O O
protected O O O  
(default) O O    
private O      

 

대상 사용가능한 접근 제어자
클래스 public, (default)
메서드 public, protected, (default), private
멤버변수
지역변수 없음

 

접근 제어자를 이용한 캡슐화

  • 캡슐화: 하나의 객체에 대해 그 객체가 특정한 목적을 위한 필요한 변수나 메소드를 하나로 묶는 것
  • 접근 제어자를 사용하는 이유
    • 외부로부터 데이터를 보호하기 위해서
    • 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
  • getter
    • 멤버변수의 값을 읽는 메서드의 이름을 'get멤버변수이름'으로 한다.
  • setter
    • 멤버변수의 값을 변경하는 메서드의 이름을 'set멤버변수이름'으로 한다.
getter, setter를 사용하는 이유 (접근제어자 활용)

멤버변수에 직접 접근하지 못하게 private으로 접근 지정자를 설정하고, public으로 getter, setter 메서드를 만들어 메서드를 통해 데이터에 접근한다.

getter, setter를 사용하면 메서드를 통해서 데이터를 접근하기 때문에, 메서드 안에서 매개변수같이 어떤 올바르지 않은 입력에 대해 사전에 처리할 수 있게 제한하거나 조절할 수 있다. (외부로부터 데이터를 보호)

 

제어자의 조합

대상 사용가능한 제어자
클래스 public, (default), final, abstract
메서드 모든 접근 제어자, final, abstract, static
멤버변수 모든 접근 제어자, final, static
지역변수 final

주의 사항

  1. 메서드에 static과 abstract를 함께 사용할 수 없다. (static은 몸통이 있는 메서드에만 사용할 수 있음)
  2. 클래스에 abstract와 final을 동시에 사용할 수 없다.
  3. abstract메서드의 접근 제어자가 private일 수 없다. (abstract는 자손클래스에서 구현해주어야 하는데 private면 자손클래스에 접근 할 수 없음)
  4. 메서드에 private과 final을 같이 사용할 필요는 없다. (접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문에 이 둘 중 하나만 사용해도 의미가 충분하다.)

source

자바의 정석