JAVA

JAVA 접근 제한자

별메아리 2023. 2. 10. 14:50
728x90

접근 제한자란 클래스 및 인터페이스 그리고 이들이 가지고 있는 멤버의 접근을 제한하기 위해 사용한다.

클래스와 인터페이스를 다른 클래스에서 사용하지 못하도록 하거나 객체 생성을 막기 위해 생성자를 호출하지 못하게 하거나 필드를 메소드를 사용하지 못하게 막아야하는 경우에도 사용된다.

접근 제한자는 3가지 종류가 있는데, public, protected, private 가 있습니다.

  • public 접근 제한자 : 단어 뜻 그대로 외부 클래스가 자유롭게 사용할 수 있게 합니다.
  • protected 접근 제한자 : 같은 패키지 또는 자식 클래스에서 사용할 수 있게 합니다.
  • private 접근 제한자 : 단어 뜻 그대로 개인적인 것이라 외부에서 사용될 수 없도록 합니다.

위 3가지 접근 제한자가 적용되지 않으면 default 접근 제한을 가집니다.

  • default 접근 제한 : 같은 패키지에 소속된 클래스에서만 사용할수 있게 합니다.

public 접근 제한

클래스를 public 접근 제한자를 붙였다면 클래스는  public 접근 제한을 가집니다. 클래스가 public 접근 제한을 가지면, 같은 패키지 뿐만 아니라 다른 패키지에서도 아무 제한없이 사용할 수 있습니다.

 

클래스의 접근 제한(1)

package ch06;

class A {} // default 접근 제한

클래스의 접근 제한(2)

package ch06;

public class B {
	A a; // (0) A 클래스 접근 가능(필드로 선언할 수 있음)
}

클래스의 접근 제한(3)

package ch07; // 패키지가 다름

public class c {
 A a; //(x)
 B b; //(0)
}

C클래스는 A클래스와 다른 패키지이므로 default 접근 제한을 갖는 A 클래스에는 접근이 되지 않지만, public 접근 제한을 갖는 B 클래스는 접근이 가능합니다. 그래서 C 클래스에서 B 클래스를 이용하여 필드선언 및 생성자/메소드 내부에서 변 수 선언이 가능합니다.

 

생성자의 접근 제한

객체를 생성하기 위해서는 new 연산자로 생성자를 호출합니다. 하지만 생성자를 어디에서나 호출할 수는 없습니다.

생성자가 어떤 접근 제한을 갖느냐에 따라 호출 가능 여부가 결정됩니다.

생성자는 다음과 같이 public, protected, default, private 접근 제한을 가집니다.

Public class ClassName{
//public 접근 제한
public ClassName(...) {...}

//protected 접근 제한
protected ClassName(...) {...}

//default 접근 제한
ClassName(...) {...}

//private 접근 제한
private ClassName(...) {...}
}
  • public 접근 제한

public 접근 제한은 모든 패키지에서 아무런 제한 없이 생성자를 호출할 수 있도록 합니다.

  • protected 접근 제한

protected 접근 제한은 default 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 생성자를 호출할 수 있도록 합니다. 차이점으로 다른 패키지에 속한 클래스가 해당 클래스의 자식 클래스라면 생성자를 호출할 수 있습니다.

  • default 접근 제한

default 접근 제한은 같은 패키지에서는 아무런 제한 없이 생성자를 호출할 수 있으나, 다른 패키지에서는 생성자를 호출할 수 없도록 합니다.

  • private 접근 제한

private 접근 제한은 동일한 패키지이건 다른 패키지이건 상관없이 생성자를 호출하지 못하도록 제한합니다. 오로지 클래스 내부에서만 생성자를 호출할 수 있고 객체를 만들 수 있습니다.

 

생성자의 접근 제한 (1)

package ch06;

public class A {
	//필드
	A a1 = new A(true); //(o)
	A a2 = new A(1); //(o)
	A a3 = new A("문자열"); //(o)
	
	//생성자
	public A(boolean b) {} //public 접근 제한
	A(int b) {}            //default 접근 제한
	private A(String s) {} //private 접근 제한
}

생성자의 접근 제한(2)

package ch06; // 패키지가 동일

public class B {
	//필드
	A a1 = new A(true);   // (o)
	A a2 = new A(1);      // (o)
	A a3 = new A("문자열"); // (x)  private 생성자 접근 불가 (컴파일 에러)
}

생성자의 접근 제한(3)

package ch07; // 패키지가 다름

import ch06.A;

public class C {
	//필드
	A a1 = new A(true);   // (o)
	A a2 = new A(1);      // (x)
	A a3 = new A("문자열"); // (x)  private 생성자 접근 불가 (컴파일 에러)
}

필드와 메소드의 접근 제한

// 필드 선언
[public | protected | privte] [static] 타입 필드;
// 메소드 선언
[public | protected | privte] [static] 리턴 타입 메소드(...) {...}

 

  • public 접근 제한

public 접근 제한은 모든 패키지에서 아무런 제한 없이 필드와 메소드를 호출할 수 있도록 합니다.

  • protected 접근 제한

protected 접근 제한은 default 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 필드와 메소드를 호출할 수 있도록 합니다. 차이점으로 다른 패키지에 속한 클래스가 해당 클래스의 자식 클래스라면 필드와 메소드를 호출할 수 있습니다.

  • default 접근 제한

default 접근 제한은 같은 패키지에서는 아무런 제한 없이 필드와 메소드를 호출할 수 있으나, 다른 패키지에서는 필드와 메소드를 호출할 수 없도록 합니다.

  • private 접근 제한

private 접근 제한은 동일한 패키지이건 다른 패키지이건 상관없이 필드와 메소드를 호출하지 못하도록 제한합니다. 오로지 클래스 내부에서만 필드와 메소드를 호출할 수 있고 객체를 만들 수 있습니다.

 

필드와 메소드의 접근 제한(1)

package ch06;

public class A2 {
	// 필드
	public int field1;  // public 접근 제한
	int field2;         // default 접근제한
	private int field3; // private 접근제한
	
	// 생성자
	public A2() {
		field1 = 1; //(0)
		field1 = 2; //(0)
		field1 = 3; //(0)
		
		method1();  //(0)
		method2();  //(0)
		method3();  //(0)
	}
	// 메소드
	public void method1() {}  // public 접근 제한
	void method2() {}         // default 접근제한
	private void method3() {} // private 접근제한
	}

A2 클래스 내부에서는 접근 제한과는 상관없이 필드와 메소드를 모두 사용할 수 있습니다.

 

필드와 메소드의 접근 제한(2)

package ch06;

public class B2 {
	public B2() {
		A2 a = new A2();
		a.field1 = 1;  // (o)
		a.field2 = 1;  // (o)
		a.field3 = 1;  // (x)
		
		a.method1();	// (o)
		a.method2();	// (o)  
		a.method3();    // (x)  private 메소드 접근 불가 (컴파일 에러)
	}
}

패키지가 동일한 B2 클래스에서는 A2 클래스의 private 필드와 메소드를 제외한 다른 필드와 메소드는 사용할 수 있습니다.  하지만 패키지가 다르면 A2 클래스의 필드와 메소드는 사용할 수 없습니다.

 

Getter와 Setter 메소드

일반적으로 객체 지향 프로그래밍에서는 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막습니다. 그 이유는 외부에서 마음대로 변경할 경우 객체의 무결성이 깨질 수 있기 때문입니다. 그 예로 자동차의 속력은 음수가 될수 없지만 음수가 되면 무결성이 깨집니다. 이런 문제점을 해결하기위해 객체 지향 프로그래밍에서는 메소드를 통해서 필드를 변경하는 것을 선호합니다. 필드는 외부에서 접근할 수 없도록 막고 메소드는 공개해서 외부에서 메소드를 통해 필드에 접근하도록 유도합니다. 그 이유는 메소드는 매개값을 검증해서 유효한 값만 객체의 필드로 저장할 수 있기 때문입니다. 이러한 역활을 하는 메소드가 Setter 입니다.

 

예를 들어 자동차의 속도를 setSpeed() 메소드로 변경할 경우 다음과 같이 검증 코드를 작성할 수 있습니다.

void setSpeed(double speed){
if(speed <0) {
this.speed = 0;
retrun;               // 매개값이 음수일 경우 speed 필드에 0으로 저장하고, 메소드 실행 종료
} else {
this.speed = speed;
 }
}

외부에서 객체의 데이터를 읽을 때도 메소드를 사용하는 것이 좋습니다. 그 이유는 필드값을 직접 사용하면 부적절한 경우도 있기 때문입니다. 이럴 땐 메소드로 필드 값을 가공한 후 외부로 전달하면 됩니다. 이런 메소드가 바로 Getter입니다.

 

예를 들어 자동차의 속도를 마일에서 km 단위로 환산해서 외부로 리턴해주는 getSpeed() 를 다음과 같이 작성할 수 있습니다.

double getSpeed() {
double km = speed*1.6;  // 필드값인 마일을 km 단위로 환산 후 외부로 리턴
return km;
}

클래스를 선언할 때 가능하다면 필드를 private로 선언해서 외부로부터 보호하고, 필드에 대한 Setter와 Getter 메소드를 작성해서 필드값을 안전하게 변경/사용하는 것이 좋습니다.

 

Setter와 Getter 선언하는 방법

private 타입 fiedName;  // 필드 접근 제한자:private

//Getter
public 리턴 타입 getFieldName() { // 접근제한자:public , 리턴타입:필드타입 , 메소드이름:get+필드이름(첫 글자는 대문자) , 리턴값:필드값
	return fieldName;
}

//Setter
public void setFiedName(타입 fieldName){ // 접근제한자:public , 리턴타입:void , 메소드이름:get+필드이름(첫 글자는 대문자) , 매개 변수 타입:필드 타입
	this.fieldName =fieldName;
}

필드 타입이 boolean일 경우에는 Getter는 get 으로 시작하지 않고 is로 시작하는 것이 관례입니다.

예를 들어 stop 필드으 Getter 와 Setter는 다음과 같이 작성할 수 있습니다.

private boolean stop; // 필드 접근 제한자 : private

//Getter
public boolean isStop() { // 접근제한자:public , 리턴타입:필드타입 , 메소드이름:get+필드이름(첫 글자는 대문자) , 리턴값:필드값
	return stop;
}

//Setter
public void setStop(boolean stop) {  // 접근제한자:public , 리턴타입:void , 메소드이름:get+필드이름(첫 글자는 대문자) , 매개 변수 타입:필드타입
	this.stop = stop;
}

만약 외부에서 필드값을 읽을 수만 읽고 변경하지 못하도록 하려면(읽기전용) Getter 메소드만 선언해도 좋고, 아니면 Setter 메소드가 private 접근 제한을 갖도록 선언해도 좋습니다.

 

예제)

Getter와 Setter 메소드 선언

 

package ch06;

public class Car9 {
	//필드
	private int speed;
	private boolean stop;
	
	//생성자
	
	//메소드
	public int getSpeed() {
		return speed;
	}
	public void setSpeed(int speed) {
		if(speed <0) {
			this.speed = 0;
			return;
		}else {
			this.speed = speed;
		}
	}
	
	public boolean isStop() {
		return stop;
	}
	public void setStop(boolean stop) {
		this.stop = stop;
		this.speed = 0;
	}
}

Getter와 Setter 메소드 사용

package ch06;

public class Car9Example {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Car9 myCar = new Car9();
		
		// 잘못된 속도 변경
		myCar.setSpeed(-50);
		
		System.out.println("현재 속도: "+ myCar.getSpeed());
		
		//올바른 속도 변경
		myCar.setSpeed(60);
		
		//멈춤
		if(!myCar.isStop()) {
			myCar.setStop(true);
		}
		
		
		System.out.println("현재 속도: "+ myCar.getSpeed());
	}
}
728x90