티스토리 뷰

제어자

  • 클래스와 클래스의 멤버(멤버변수, 메서드)에 부가적인 의미를 부여한다.
  • 하나의 대상에 여러 제어자를 같이 사용 가능(접근 제어자는 하나만)
더보기

클래스와 클래스의 멤버(멤버변수, 메서드)에 부가적인 의미를 부여한다.

  • 제어자의 종류
접근 제어자     public, protected, (default), private
그 외                 static, final, abstract, native, transient, synchronized, volatile, strictfp

하나의 대상에 여러 제어자를 같이 사용 가능(접근 제어자는 하나만)

public class ModifierTest {
    public static final int WIDTH = 200;     // 제어자 - public, static, final
    public static void main(String[] args) { // 제어자 - public, static
        System.out.println("WIDTH=" + WIDTH);
    }
}

 

 

static - 클래스의, 공통적인

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

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

어떤 대상에 붙느냐에 따라 의미가 달라진다.
class staticTest {
	static int width = 200;          // 클래스변수(static변수)
	static int height = 120;         // 클래스변수(static변수)
	
	static {                         //  클래스 초기화 블럭
		// static변수의 복잡한 초기화 수행
	}

	static int max(int a, int b) {   // 클래스메서드(static메서드)
		return a > b ? a : b;	     // 인스턴스멤버(iv, im) 사용 불가
	}
}

iv멤버들은 객체 생성 후에 사용할 수 있기 때문에 클래스메서드에서 인스턴스멤버를 사용할 수 없다. 

 

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

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

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

어떤 대상에 붙느냐에 따라 의미가 달라진다.
final class FinalTest {            // 조상이 될 수 없는 클래스 - String, Math
    final int MAX_SIZE = 10;       // 값을 변경할 수 없는 멤버변수(상수)

    final void getMaxSize() {      // 오버라이딩할 수 없는 메서드(변경불가)
        final int LV = MAX_SIZE;   // 값을 변경할 수 없는 지역변수(상수)
		return MAX_SIZE;
	}
}

 final class는 상속계층도의 제일 마지막 계층의 클래스를 의미한다. 대표적인 final class에는 String, Math클래스가 있다. 보안 이슈 때문에 final class가 되는 것이다. 상속이 가능하면 자손에서 조상 멤버에 접근할 수 있기 때문에 그것을 막으려고 final로 하는 것이다. Math클래스는 static메서드 집합이기 때문에 굳이 상속받아서 쓸 필요가 없다.

 

abstract - 추상의, 미완성의

  • abstract이 사용될 수 있는 곳 - 클래스, 메서드
더보기

abstract이 사용될 수 있는 곳 - 클래스, 메서드

어떤 대상에 붙느냐에 따라 의미가 달라진다.
abstract class AbstractTest {  // 추상 클래스(추상 메서드를 포함한 클래스)
	abstract void move();      // 추상 메서드(구현부가 없는 메서드)
}

AbstractTest a = new AbstractTest(); // 에러. 추상 클래스의 인스턴스 생성불가
                                     // 미완성 설계도라 제품 생성 불가
                                     // 추상클래스 상속받아 완성된 클래스 만든 후
                                     // 객체 생성 가능

 

접근제어자(access modifier)

  • private        : 같은 클래스 내에서만 접근 가능
  • (default)     : 같은 패키지 내에서만 접근 가능
  • protected  :  i)  같은 패키지 내에서,
                          ii) 다른 패키지의 자손 클래스에서 접근 가능
  • public          : 접근 제한이 전혀 없다.
더보기
class AccessModifierTest {  // class 앞에는 public || (default) 두 접근제어자만 사용 가능
    int iv;         // 멤버변수(인스턴스변수)
    static int cv;  // 멤버변수(클래스변수)
    
    void method() {}  // 인스턴스메서드   
}  // 멤버 앞에는 public, protected, (default), private 네 접근제어 사용 가능

예제pkg1.MyParentTest

더보기

[테스트] 4개의 멤버에 대해, 밖에서 접근할 수 있는지 테스트

package pkg1;

class MyParent {
	private   int prv; // 같은 클래스
			  int dft; // 같은 패키지
	protected int prt; // 같은 패키지 + 자손
	public 	  int pub; // 접근제한없음
	
	public void printMembers() {
		System.out.println(prv);
		System.out.println(dft);
		System.out.println(prt);
		System.out.println(pub);
	}
}

public class MyParentTest {
	public static void main(String[] args) {
		MyParent mp = new MyParent();
		mp.prv = 1;  // 에러.
		mp.dft = 2;  // ok.
		mp.prt = 3;  // ok.
		mp.pub = 4;  // ok.
		
		mp.printMembers();
	}
}

MyParent클래스의 멤버 중 private 접근제어자를 사용한 멤버변수 prv는 다른 클래스 MyParentTest에서 접근할 수 없기 때문에 에러가 발생한다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	The field MyParent.prv is not visible

	at pkg1.MyParentTest.main(MyParentTest.java:20)

MyParent클래스의 접근제어자는 (default)이다.

따라서 다른 패키지인 pkg2의 MyChild클래스가 MyParent를 상속받을 수 없다.

다른 패키지에서 MyParent를 사용하기 위해서는 MyParent클래스에 public을 붙여야 한다.

하나의 소스 파일에 둘 이상의 public class가 존재하면 안 된다. 

소스파일 이름을 MyParentTest에서 MyParent로 변경해야 한다.

소스파일 이름과 public 클래스이름이 일치해야 하기 때문이다.

Update references 체크 해제 후 Finish 버튼을 누른다.

(17Line) MyParent 클래스이름을 MyParentTest로 변경한다.

여전히 pkg2의 MyChild클래스에서 MyParent를 상속하지 못 하고 에러가 발생한다.

MyParent cannot be resolved to a type   <-   MyParent를 찾지 못하고 있다.

Ctrl + Shift + o를 입력한다.

자동으로 클래스의 패키지를 지정해준다.

import문을 쓰지 않으면 pkg1.MyParent로 패키지명과 함께 클래스를 작성하면 된다.

prv : 접근제어자로 private를 사용했기 때문에, 같은 클래스 안이 아니라면 접근이 불가능하다.

dft : 접근제어자로 (default)를 사용했기 때문에, 같은 패키지 안이 아니라면 접근이 불가능하다. (두 곳에서 에러 발생)

두 줄에 대해 주석처리해주자.

에러를 확인했으니, 해당 줄을 주석처리해주자.

실습 완료

접근제어자의 접근 범위는 위와 같다.

 

 

접근제어자를 사용하는 이유

  1. 외부로부터 데이터를 보호하기 위해서(캡슐화)
  2. 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서
더보기

1. 외부로부터 데이터를 보호하기 위해서(캡슐화)

  • 멤버변수의 접근제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다.
  • 접근제어자를 public으로 한 메서드를 통해 간접적으로 멤버변수에 접근하도록 코딩해야 한다.
public class Time {
    public int hour;
    public int minute;
    public int second;
}

Time클래스의 인스턴스변수(iv)들의 접근제어자가 전부 public이면, '접근제한'이 없으므로.

Time t = new Time();
t.hour = 25;  // 멤버변수에 직접 접근

위와 같이 멤버변수에 직접 접근이 가능해진다.

Integer의 범위가 +- 20억이다.

이 범위의 어떤 값을 넣어도 에러가 나지 않는다.

hour의 경우, 범위가 0~23

minute의 경우, 범위가 0~59

second의 경우, 범위가 0~59이다.

각 iv들이 범위 내에서 값이 유지가 되어야 하는데, 대입연산자(=)로 직접 접근이 가능하기 때문에, 범위 외의 값들이 저장되는 것을 막을 수가 없다. 데이터 보호가 안 된다. hour, minute, second라는 데이터(iv)들을 보호하려면, 다음과 같이 코드를 작성해야 한다.

public class Time {
    private int hour;                     // 접근 제어자를 private으로 하여
    private int minute;                   // 외부에서 직접 접근하지 못하도록 한다.
    private int second;                   // 멤버변수는 private으로 한다.
    
    // 겟터(getter) & 셋터(setter)
    public int getHour() { return hour; } // 반면, 메서드는 public으로 한다.
    public void setHour(int hour) {
    	if(hour < 0 || hour > 23) return; // 값을 보호
        this.hour = hour;
    }
    public int getMinute() { return minute; }
    public void setMinute(int minute) { 
    	if(minute < 0 || minute > 59) return;
        this.minute = minute;
    }
    public int getSecond() { return second; }
	public void setSecond(int second) {
    	if(second < 0 || second > 59) return;
        this.second = second;
    }
}

public class TimeTest {
	public static void main(String[] args) {
    	Time t = new Time();
//      t.hour = 25;    // 멤버변수에 직접 접근 (NO)
        t.setHour(25); // 멤버변수에 간접 접근 값 보호 (YES)
        t.setHour(21); // 멤버변수에 간접 접근 값 저장 (YES)
    }
}

 

  • iv의 접근제어자를 private으로 하고, 메서드의 접근제어자는 public으로 한다.
  • 멤버변수에 대해 외부에서 직접 접근 못하도록 하지만, 메서드는 외부에서 직접 접근할 수 있도록 한다.
  • 메서드를 통해서만 iv에 접근하도록 즉, 간접 접근만 허용하도록 코딩해야 한다.
Time t = new Time();
t.setHour(25);

 

이런 식으로 메서드를 통해서 iv에 간접적으로만 접근 가능하도록 코딩한다.

인수에 25를 지정하여 setHour메서드를 호출하면, 메서드에서 제한한 iv의 범위를 벗어나기 때문에, iv값이 변경되지 않는다. 

이러한 방식으로 데이터를 보호하는 것이다.

 

Time t = new Time();
t.setHour(21);

인수에 21을 지정하여 setHour메서드를 호출하면, 메서드에서 제한한 iv의 범위 안에 있는 데이터가 들어오기 때문에, iv값이 변경된다.

접근제어자 private을 지정하여 직접 접근을 막고, 메서드를 통한 간접 접근만 허용하는 것을 접근제어자를 통한 '캡슐화'라 한다. 캡슐화를 이용하는 이유는 데이터를 보호하기 위함이다.

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

 

예제TimeTest

더보기

Getter & Setter

  • getter : 멤버변수의 값을 읽는 메서드(get멤버변수이름)
  • setter : 멤버변수의 값을 변경하는 메서드(set멤버변수이름)
// getter & setter
public void setHour(int hour) {
    if(hour < 0 || hour > 23) return;
    this.hour = hour;
}

public int getHour() {
    return hour;
}

 

조건을 별도의 메서드로 추출

  • [Eclipse] Alt  + Shift + M(isNotValidHour)
  • 코드에 주석 없이 의미가 잘 드러나게 하려면, 읽기 좋은 코드로 하려면 이렇게 하는 것이 좋다.
// getter & setter
public void setHour(int hour) {
    if(isNotValidHour(hour)) return;
    this.hour = hour;
}

// 매개변수로 넘겨진 hour가 유효한지 확인해서 알려주는 메서드
private boolean isNotValidHour(int hour) {
    return hour < 0 || hour > 23;
}
public int getHour() {
    return hour;
}

 

메서드의 접근제어자를 private으로 지정하면 좋은 이유

  • 접근범위는 최소화시킨다. 필요 시 넓히도록 한다. ⭐
// 매개변수로 넘겨진 hour가 유효한지 확인해서 알려주는 메서드
private boolean isNotValidHour(int hour) {
    return hour < 0 || hour > 23;
}

 

굳이 메서드의 접근제어자를 public으로 지정하면 클래스 외부에서 이 메서드를 사용할 수 있게 된다.

내부에서만 사용하는 메서드의 경우 private으로 접근제어를 지정하는 것이 좋다.

메서드를 수정할 일이 생길 경우, 변경사항을 테스트할 때 접근제어자 private이라면 '메서드가 private이므로 이 클래스 내부에서만 테스트를 하면 되겠구나' 판단할 수 있다. 테스트할 범위가 줄어든다

 우리가 코드를 변경할 경우, 프로그램이 깨졌는지 테스트를 해봐야 하는데, public으로 접근범위가 지정된 메서드의 경우, 메서드가 사용된 클래스 외부도 다 테스트를 해야 한다. 따라서, 접근제어자의 범위를 최대한 좁히는 것이 유리하다.

 


🍪

📄 - 객체지향개념 암기노트 바로가기

'Java의 정석_기초편' 카테고리의 다른 글

클래스와 객체  (0) 2022.10.10
객체지향 언어  (0) 2022.10.10
추상 클래스(abstract class)  (0) 2022.10.09
다형성(polymorphism)  (0) 2022.10.07
package와 import  (0) 2022.10.07
댓글
공지사항