본문 바로가기
언어/자바의 정석

[자바의 정석] Chapter 7 객체 지향 프로그래밍II (4) - 제어자(modifier)

by chan10 2021. 2. 16.

제어자란?

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

접근 제어자

public, protected, default, private

그 외

static, final, abstract, native, transient, synchronized, volatile, strictfp

o  제어자는 클래스나 멤버 변수에 주로 사용되며, 하나의 대상에 여러 제어자를 조합하여 사용할 수 있지만 접근 제어자는 한 번에 하나만 선택해서 사용할 수 있다.

 

static – 클래스의, 공통적인

o  static클래스의또는 공통적인의미로 인스턴스 변수는 인스턴스 별로 다른 값을 유지하지만 클래스(static)변수는 모든 인스턴스가 공유하기에 하나의 값을 가진다.

o  static이 붙은 멤버변수, 메소드, 초기화 블록은 클래스에 관계된 것이기에 인스턴스를 생성하지 않고 사용할 수 있다.

o  인스턴스 메서드와 static메서드의 차이는 메소드 내에서 인스턴스 멤버를 사용하는지 여부에 있다.

-       static이 사용될 수 있는 곳 멤버 변수, 메서드, 초기화 블록

제어자

대상

의미

static

멤버변수

- 모든 인스턴스에 공통적으로 사용되는 클래스변수가 된다.

- 클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.

- 클래스가 메모리에 로드 될 때 생성된다.

메서드

- 인스턴스를 생성하지 않고도 호출이 가능한 static메서드가 된다.

- static메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다.

o  static메서드는 인스턴스 생성 없이 호출이 가능하여 속도도 더 빠르기에 인스턴스 멤버를 사용하지 않는 메서드는 static을 붙여서 static메서드를 사용하는 것을 고려한다.

 

final – 마지막의, 변경될 수 없는

o  final마지막의또는 변경될 수 없는의 의미를 가지고 있으며 거의 모든 대상에 사용 가능하다.

-       final이 사용될 수 있는 곳 클래스, 메서드, 멤버변수, 지역변수

제어자

대상

의미

final

클래스

- 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.

- 그래서 final로 지정된 클래스다른 클래스의 조상이 될 수 없다.

메서드

변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.

멤버 변수

변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.

지역 변수

 

생성자를 이용한 final멤버 변수의 초기화

o  final이 붙은 변수는 일반적으로 선언과 동시에 초기화를 하지만 인스턴스 변수의 경우 생성자에서 초기화 될수록 할 수 있다. 매개변수를 받는 생성자를 선언하여 인스턴스 생성 시 매개변수로 넘겨 받은 값으로 final이 붙은 멤버 변수를 초기화 할 수 있다.

class Card{
//에러, 상수는 선언과 동시에 초기화를 해주어야 한다.
//    final int NUMBER;
final int NUMBER=10;
}
 
 

class Card{
    final int NUMBER;
    
    Card(int num){
        NUMBER = num;
    }
}

 

-       오른쪽 코드를 사용함으로써 인스턴스마다 final이 붙은 멤버 변수 값을 다르게 가져갈 수 있다.

 

o  final 메서드, 클래스 예시

메서드에 final 사용 시
class Person {
    // 메서드 final 적용
    public final void update() {System.out.println("Person");}
}
 
class Child extends Person{//상속은 가능
    @Override
    public void update() {    //에러, 오버라이딩 불가 
        System.out.println("Child");
    }
}
클래스에 final 사용 시
// 클래스 final 적용으로 인해 상속이 불가능하다
final class Person {
    public void update() {System.out.println("Person");}
}
 
class Child extends Person{    //에러, 상속 불가
    @Override
    public void update() { 
        System.out.println("Child");
    }
}

 

 

abstract – 추상의, 미완성의

o  abstract추상의’, ‘미완성의의 의미로 메서드 선언부만 작성하고 실제 수행내용인 구현부는 작성하지 않은 추상 메서드를 선언하는데 사용한다.

-       abstract가 사용될 수 있는 곳 클래스, 메소드

제어자

대상

의미

abstract

클래스

클래스 내에 추상 메서드가 선언되어 있음을 의미한다.

메서드

선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알린다.

o  추상 클래스는 미완성의 메서드가 존재하는 미완성 설계도이므로 인스턴스를 생성할 수 없다.

 

o  드물지만 일반적인(완성된) 클래스도 abstract를 붙여 추상 클래스도 만드는 경우도 있다. 예를 들어 jave.awt.event.WindowAdapter는 아무런 내용이 없는 메서도만 정의되어 있다. 이런 클래스는 인스턴스를 생성해봐야 할 수 있는 것이 아무것도 없기에 인스턴스를 생성하지 못하게 제어자 ‘abstract’를 붙여놓았다.

o  이 클래스 자체로는 의미가 없지만 다른 클래스가 이 클래스를 상속받아 일부 원하는 메서드만 오버라이딩해도 된다는 장점이 있다.

 

접근 제어자(access modifier)

o  접근 제어자는 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다. 접근 제어자가 지정되어 있지 않다면 default접근 제한임을 뜻한다.

 

접근 제어자가 사용될 수 있는 곳 클래스, 멤버변수, 메서드, 생성자

private

같은 클래스 내에서만 접근이 가능하다

default

같은 패키지 내에서만 접근이 가능하다.

protected

같은 패키지 내에서 접근 가능하다. (상속 관계 X)

패키지 관계없이 상속관계의 자손클래스에서 접근이 가능하다.

public

접근 제한이 없다. (어디서나 접근 가능)

 

제어자

같은 클래스

같은 패키지

자손클래스

전체

private

O

O

O

O

default

O

O

O

 

protected

O

O

 

 

public

O

 

 

 

접근 범위 : public > protected > (default) > private

대상

사용가능한 접근 제어자

클래스

public, (default)

메서드

public, protected, (default), private

멤버변수

지역변수

없음

대상에 따라 사용할 수 있는 접근 제어자

 

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

o  클래스나 멤버, 주로 멤버에 접근 제어자를 사용하는 이유는 클래스의 내부에 선언된 데이터를 외부에서 변경하지 못하도록 보호하기 위해 접근 제한을 한다. 이것을 데이터 감추기(data hiding)이라고 하며 객체지향 개념의 캡슐화(encapsulation)에 해당되는 내용이다.

o  또 다른 이유는 외부에서 접근할 필요가 없는 즉, 클래스 내에서만 사용되는 임시 사용 멤버 변수, 부분 작업 메서드 등을 감추기 위함으로써 복잡성을 줄일 수 있다. 이 역시 캡슐화에 해당하는 내용이다.

o  접근 제어자를 사용하는 이유를 정리하면 다음과 같다.

-       외부로부터 데이터를 보호하기 위해서

-       외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서

package JavaProject;
 
class Time {
    private int hour, minute, second;
    
    Time(int hour, int minute, int second){
        setHour(hour);
        setMinute(minute);
        setSecond(second);
    }
    
    public int getHour() { return hour;}
    public int getminute() { return minute;}
    public int getsecond() { return second;}
    
    public void setHour(int hour) {
        if(hour<0 || hour>23return;
        this.hour = hour;
    }
    public void setMinute(int minute) {
        if(minute<0 || minute>59return;
        this.minute = minute;
    }
    public void setSecond(int second) {
        if(second<0 || second>59return;
        this.second = second;
    }
    public String toString() {
        return hour + ":" + minute + ":" + second;
    }
}
 
public class Test {    
    public static void main(String[] args) {
        Time t = new Time(11,16,58);
        System.out.println(t);
//        t.hour = 13;    에러, 변수 hour에 직접 접근 할 수 없다.
        t.setHour(t.getHour()+1);    //현재 시간에서 1시간 후로 변경    
        System.out.println(t);    //System.out.println(t.toString());과 같다.
    }
}

-       만일 상속을 통해 확장될 것이 예상되는 클래스라면 멤버 변수의 접근 제어자 private대신에 protected를 사용한다. private은 자손 클래스도 접근이 불가능 하기 때문이다.

-       보통 멤버 변수 값을 읽는 메서드 이름을 ‘get매게변수이름으로 하고 멤버 변수 값을 변경하는 메서드의 이름을 ‘set매게변수이름으로한다. 암묵적인 규칙으로 이것을 getter, setter라고 부른다.

 

생성자의 접근 제어자

o  생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다. 생성자의 접근 제한자를 private로 지정하면 외부에서 생성자에 접근이 불가하기에 인스턴스 생성을 할 수 없지만 내부적으로 생성은 가능하다.

o  대신 인스턴스를 생성해서 반환해주는 public메서드를 제공함으로써 외부에서 이 클래스의 인스턴스를 사용하도록 할 수 있다. 다만 이 메서드는 public인 동시에 static이어야 한다.

package JavaProject;
 
class Singleton{
    // getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다. 
    private static Singleton s = new Singleton();
    private Singleton() { }
    
    // 인스턴스를 생성하지 않고 호출해야 하므로 static이어야 한다.
    public static Singleton getInstance() {
        if(s==null) s = new Singleton();
        return s;
    }
}
 
public class Test {    
    public static void main(String[] args) {
        Singleton s= Singleton.getInstance();
        Singleton s1= Singleton.getInstance();
        System.out.println(s);
        System.out.println(s1);
    }
}

o  이처럼 public메서드를 통해 인스턴스에 접근하게 함으로써 사용할 수 있는 인스턴스의 개수를 제한할 수 있다.

o  생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다. 자손 클래스에서 인스턴스를 생성 시 조상 클래스의 생성자를 호출하는데 생성자의 접근 제어자가 private이므로 자손 클래스에서도 호출할 수 없기 때문이다. 그래서 클래스 앞에 final을 붙여 상속할 수 없는 클래스 라는 것을 알린다.

public final class Math{
    private Math() { }
}
 

 

제어자(modifier)의 조합

o  접근 제어자 static, final, abstract에 대해서 학습했다. 더 많은 제어자가 있지만 이는 후에 학습한다.

o  제어자가 사용될 수 있는 대상을 중심으로 제어자를 정리했다.

대상

사용가능한 제어자

클래스

public, (default), final, abstract

메서드

모든 접근 제어자, final, abstract, static

멤버변수

모든 접근 제어자, final, static

지역변수

final

 

제어자를 사용할 때 주의 사항

1.     메서드에 staticabstract를 함께 사용할 수 없다.

static메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문이다.

 

2.     클래스에 abstractfinal을 동시에 사용할 수 없다.

클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고 abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모순되기 때문이다.

 

3.     abstract메서드의 접근 제어자가 private일 수 없다.

abstract메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손 클래스에서 접근할 수 없기 때문이다.

 

4.     메서드에 privatefinal을 사용할 필요는 없다.

접근 제어자가 private인 메서드는 오버라이딩 될 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.