제이제이
article thumbnail

 

정보 은닉


🤔 정보 은닉이란?

💡 자바에서 의미하는 ‘정보’는 클래스의 ‘인스턴스 변수(멤버, 필드)’를 의미합니다.
  • 따라서 정보 은닉을 한다는 것은 인스턴스 변수를 숨기는 것을 의미합니다.

 

  • 이와 관련되어 원의 반지름과 넓이를 구하는 다음의 코드를 살펴봅시다.

 

💻  예시 코드

class Circle{
    double rad = 0; //원의 반지름
    final double PI = 3.14;

    public Circle(double r) {
        setRad(r); //아래의 정의된 setRad 메소드 호출을 통한 초기화
    }

    public void setRad(double r){
        if(r<0 ){ //반지름은 0보다 작을 수 없으므로
            rad = 0;
            return; //메소드를 빠져나감
        }
        rad = r;
    }

    public double getArea(){
        return (rad * rad) *PI; //원의 넓이를 반환
    }
}

public class UnsafeCircle {
    public static void main(String[]args){
	      // 객체 생성
        CircleEx c = new CircleEx(1.5);
        System.out.println(c.getArea());

        c.setRad(2.5);
        System.out.println(c.getArea());
        c.setRad(-3.3);
        System.out.println(c.getArea());
				
			  // 문제가 되는 부분
        c.rad = -4.5;
        System.out.println(c.getArea());
    }
}

 

📸 출력 결과

7.065
19.625
0.0
63.585

 

👩🏻‍🏫 설명

  • 정상적으로 실행되어 출력되었으나 다음과 같은 부분에 대해 문제가 발생됩니다.
  • 원의 초기화를 진행한 메소드를 살펴봅시다
  • 반지름의 길이 rad에 0보다 작은 값이 저장되지 않도록 메소드를 정의 했습니다.
public void setRad(double r){
		if(r<0){
			rad = 0; //반지름은 0보다 작을 수 없으므로
			return; //메소드를 빠져나감
 		 }
	rad = r;
}

 

⚠️ 문제 발생 부분

  • 그러나 다음 부분에서 반지름 길이가 0보다 작은 -4.5가 입력되는 잘못된 접근이 일어납니다.
c.rad = -4.5; //옳지 않은 접근 방법

 

  • 이렇게 필드에 직접적인 접근이 허용되면 컴파일 과정과 실행 결과에서 드러나지 않은 문제가 발생됩니다.

-> 때문에 “정보 은닉” 해서 클래스를 사용할 필요가 있습니다.

  • 위의 코드를 다음과 같이 변경해 봅시다.

 

💻 예시 코드

class Circle{
    private double rad = 0;
    final double PI = 3.14;

    public Circle(double r){
        setRad(r);
    }

	/*
	겟터와 셋터
	*/
    public void setRad(double r){
        if(r<0){
            rad = 0;
            return;
        }
        rad = r;
    }

    public double getRad(){
        return rad;
    }
    public double getArea(){
        return (rad *rad) * PI; //원의 넓이 반환
    }
}

public class InforHideCircle {
    public static void main(String[]args){
        //객체 생성
        Circle2 c = new Circle2(1.5);
        System.out.println("반지름: " + c.getRad());
        System.out.println("넓이: " + c.getArea() + "\\n");

        c.setRad(3.4);
        System.out.println("반지름: " + c.getRad());
        System.out.println("넓이: " + c.getArea());
    }
}

 

📸 출력 결과

반지름: 1.5
넓이: 7.065

반지름: 3.4
넓이: 36.2984

 

👩🏻‍🏫 설명

  • 다음과 같은 부분이 변경되었습니다.

 

변경된 부분

접근 지시자(Private - 정보 은닉)

  • 클래스의 Circle의 인스턴스 변수가 private으로 선언되었습니다.
private double rad = 0; //클래스 내부 접근만 허용

 

  • 의미하는 바는 다음과 같습니다.

-> "변수 rad는 클래스 내부에서만 접근을 허용하겠다!"

 

  • 클래스 내부에 정의된 메소드를 통해서만 접근을 허용하겠다는 의미입니다.
  • 따라서 외부에서 private으로 선언된 rad에 접근할 경우 컴파일 오류가 발생합니다.
  • 따라서 변수 rad에 접근하기 위한 세터(Setter), 게터(Getter) 메소드를 만들어 접근해야 합니다.

 

게터(Getter)

  • 인스턴스 변수의 값을 참조하는 용도로 정의된 메소드입니다.
  • 변수의 이름이 name일때, 메소드 이름은 일반적으로 getName으로 짓습니다.

 

세터(Setter)

  • 인스턴스 변수의 값을 설정하는 용도로 정의된 메소드입니다.
  • 변수의 이름이 name일 때, 메소드의 이름은 setName으로 짓습니다.

 

변경된 부분(게터와 세터)

public void setRad(double r) { //rad에 값을 저장(수정)
		if(r<0){
			rad = 0;
			return;
	}
	rad = r;
}
public double getRad() { //rad에 저장된 값을 반환
		return rad;
}

 

 

접근 수준 지시자 (Access-Ievel Modifiers)


🤔 접근 수준 지시자란?

  • 변수, 메소드 또는 클래스의 접근 허용 수준을 결정할 때, 선언하는 키워드입니다.

-> 접근 수준 지시자는 총 4가지가 있습니다.

 

🔥 public, protected, private, default

 

  • 접근 수준 지시자를 선언할 수 있는 대상은 다음의 2가지로 구분됩니다.

1. 클래스의 정의

2. 클래스의 인스턴스 변수와 메소드

 

  • 클래스 정의를 대상으로는 다음의 두가지 선언이 가능합니다.

-> 클래스 정의 대상: pulic, default

 

  • 클래스의 인스턴스 변수와 메소드를 대상으로는 다음의 4가지 선언이 모두 가능합니다.

-> 클래스의 인스턴스 변수와 메소드 대상: public, protected, private, default

 

  • 먼저 클래스의 정의를 대상으로 public과 default 선언이 갖는 의미에 대해서 살펴봅시다.

-> 다음과 같이 클래스가 public으로 선언되면 패키지의 위치와 상관 없이 어디서든 해당 클래스의 인스턴스를 생성할 수 있습니다.

public clas AAA{ //클래스의 public 선언
  ~~~
}

 

반면 default로 선언되면 동일 패키지로 묶인 클래스 내에서만 인스턴스 생성이 가능합니다.

class ZZZ{ //클래스의 default 선언
   ~~~~
}

 

정리

  • public로 선언이 된 클래스: 어디서든 인스턴스의 생성이 가능하다.
  • default로 선언이 된 클래스: 동일 패키지로 묶인 클래스 내에서만 인스턴스 생성이 가능하다.

 

  • 아래의 예를 통해 더 자세히 살펴봅시다.

 

💻 예시 코드

package part9.zoo;

//Duck은 default로 선언되었으므로 동일 패키지 내에서만 인스턴스 생성 가능
class Duck {
    //빈 클래스
}

//Cat은 public으로 선언되었으므로 어디서든 인스턴스 생성 가능
public class Cat {
    public void makeCat(){
        //Duck과 같은 패키지로 묶여 있으니 Duck 인스턴스 생성 가능
        Duck quack = new Duck();
    }
}
package part9.animal;

public class Dog {
    public void makeCat(){
        //Cat은 public으로 선언되었으므로 어디서든 인스턴스 생성 가능
        part9.zoo.Cat yang = new part9.zoo.Cat();
    }
    
    public void makeDuck(){
        //Duck은 default로 선언되었으므로 이 위치에서 인스턴스 생성 불가
        part9.zoo.Duck quck = new part9.zoo.Duck(); //컴파일 오류 발생 문장
    }
}

 

  • 또한 클래스의 Public 선언과 관련하여 다음의 두 사항을 지켜야 합니다.

1. 하나의 소스 파일에 하나의 클래스만 public으로 선언한다.

2. 소스 파일의 이름과 public으로 선언된 클래스의 이름을 일치시킨다.

  • 다음으로는 인스턴스 멤버 대상의 public, protected, private, default 선언에 대해 살펴보겠습니다.

 

💻 예시 코드

class AAA{
		public int num1;    //인스턴스 변수의 public 선언
		protected int num2; //인스턴스 변수의 protected 선언
		private int num3;   //인스턴스 변수의 private 선언
		int num4;           //인스턴스 변수의 default 선언

		public void md1() { ~~ }    //인스턴스 메소드의 public 선언
		protected void md2() { ~~~} //인스턴스 메소드의 protected 선언
		private void md3() { ~~~ }  //인스턴스 메소드의 private 선언
		void md4() { ~~~ }          //인스턴스 메소드의 default 선언
} 

 

👩🏻‍🏫 설명

  • 앞서 클래스 선언에서 public과 default 선언을 살펴봤습니다. 동일한 의미가 인스턴스 멤버에 적용됩니다.

1. 인스턴스 멤버의 public = 어디서든 접근이 가능하다.

2. 인스턴스 멤버의 default = 동일 패키지로 묶인 클래스 내에서만 접근이 가능하다.

 

  • 변수의 경우 ‘접근’일 경우 말 그대로 접근을 의미하지만, 메소드의 경우 ‘호출’을 의미합니다.
  • 이와 관련되어 아래의 코드를 살펴봅시다.

 

💻 예시 코드

package part9.zoo;

public class Cat {

    //public으로 선언된 메소드, 따라서 어디서든 호출 가능(makeSound)
    public void makeSound(){
        System.out.println("아용");
    }
    
    //default로 선언된 메소드, 따라서 동일 패키지로 묶인 클래스 내에서 호출 가능(makeHappy)
    void makeHappy(){
        System.out.println("스마일");
    }
}
package part9.animal;

public class Dog {
    public void welcome(part9.zoo.Cat c){
        c.makeSound(); //호출 가능
        c.makeHappy(); //호출 불가능
    }
}

 

  • 다음으로 앞서 살펴본 인스턴스 멤버 private의 선언에 대해 다시 보겠습니다

private 선언

🔥 클래스 내부에서만 접근이 가능하다

 

  • 아래의 코드를 통해 다시 살펴봅시다.

💻 예시 코드

class Duck{
		private int numLeg = 2; //클래스 내부에서만 접근 가능

		public void md1(){
			System.out.println(numLeg); //접근 가능
			md2(); 호출 가능
   }
		private void md2() {
			System.out.println(numLeg); //접근 가능
   }
		void md3() {
			System.out.println(numLeg); //접근 가능
			md2(); //호출 가능
   }

}

 

👩🏻‍🏫 설명

  • private으로 선언된 numLeg은 해당 클래스내에 정의된 메소드를 통해서만 접근이 가능합니다.
  • 이와 마찬가지로 private 선언으로 정의된 메소드 md2도 Duck클래스에 정의된 메소드 내에서만 호출이 가능합니다.

 

  • 다음으로는 인스턴스 멤버의 protected 선언에 대해 알아보겠습니다.

 

protected 선언

🔥 protected로 선언된 멤버는 상속 관계에 있는 다른 클래스에서 접근 가능합니다.

 

다음의 코드 살펴봅시다.

(상황)

  • AAA클래스를 ZZZ 클래스가 상속하고 있는 상태입니다. (AAA클래스는 part9.alpha 패키지에, ZZZ는 디폴트 패키지에 있습니다.)
  • AAA 클래스의 모든 인스턴스 변수와 메소드들은 상속으로 인해 ZZZ 클래스의 멤버가 됩니다.
package part9.alpha;

public class AAA {
    int num;
}
// ZZZ 클래스는 디폴트 패키지에 묶여있고, AAA 클래스는 part9.alpha 패키지에 묶여있습니다.
//클래스 AAA가 part9.alpha 패키지로 묶였으므로 part9.alpha.AAA가 되었다.
public class ZZZ extends part9.alpha.AAA{
	public void init(int n) {
		num = n; //상속된 변수 num의 접근
	}
}
  • default 선언은 동일 클래스에서만 접근이 허용되지만 protected로 선언된 멤버는 상속 관계에 있는 다른 클래스에서 접근이 가능하기 때문에 서로 다른 패키지에 있더라도 접근이 가능합니다.

 

인스턴스 멤버를 대상으로 하는 접근 수준 지시자에 대한 정리

  • 인스턴스 멤버를 대상으로 한 네가지 ‘접근 수준 지시자’에 대해 정리하면 다음의 표와 같습니다.
지시자 클래스 내부 동일한 패키지 내부 상속을 받은 클래스 그 이외의 영역
private 0 X X X
default 0 0 X X
protected 0 0 0 X
public 0 0 0 0

위의 표에서 말하는 ‘그 이외의 영역’은 ‘서로 다른 패키지에 속한 클래스에 대한 접근’를 의미합니다.

 

📒 Reference (참고 자료)


  1. 윤성우의 열혈 자바
profile

제이제이

@아사비치즈스틱

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!