DEV/Java

[java] 접근 제어자의 이해와 구현 예제(Access Modifier)

Bi3a 2023. 7. 26. 07:47

728x90

접근 제어자의 이해와 구현 예제(Access Modifier)
java 기초 깨부시기


 

접근 제어자의 이해

접근 제어자란?

선언한 클래스와 클래스의 멤버(변수와 메서드 등)에 대해 외부에서 접근할 수 있는 범위를 제어하는 키워드입니다.
객체 지향에서의 정보 은닉을 목적으로 각 정보 별 접근 허용 권한과 멤버를 구체화할 수 있습니다. 

 

접근 제어자의 종류 

접근을 허용하는 범위에 따라 총 4가지가 있으며 각 제어자 별 접근 허용 범위는 아래와 같습니다.

 

접근 제어자별 접근 허용 범위 : public > protected > default > private

구     분 해당 클래스 같은 패키지 내 클래스 자식 클래스 다른 패키지의 클래스
public  O O O O
protected  O O O X(자식 클래스는 O)
default  O O X X
private  O X X X

 

접근 제어자의 장점

  • 정보 은닉을 통해 사용자에게 불필요한 정보를 제공하지 않으므로 프로그램의 사용이 용이해짐
  • 불필요한 객체의 오용을 방지해 코드 가독성 및 프로그램 효율성 증가
  • 외부 접근으로부터 정보를 은닉해 개발 보안에 효과적

 

접근 제어자의 구현

기본 선언 방법

선언할 클래스 및 멤버 변수, 메서드 앞에 해당 접근 제어자를 붙여줍니다.
public class Class{
	public void publicMethod() {
        System.out.println("I am public");
    }

    protected void protectedMethod() {
        System.out.println("i am protected");
    }

    void defaultMethod() { // default는 별도 접근제어자 없을 시 기본적용
        System.out.println("i am default");
    }

    private String s = "I am private";
}

 

public class 는 java 파일 당 외부에 1개만 선언될 수 있으며,
protected, private 멤버는 클래스 내부에서만 선언될 수 있습니다.
클래스가 사용할 수 있는 접근제어자는 public과 default입니다. (protected, private은 불가능)
class A {
    protected int a;
    private void p(){}
}

public class B{} // 컴파일 에러 : public class가 java 파일 내 두개 존재

// protected, private으로 클래스 선언 불가능
protected class C{} // 컴파일 에러
private class D{} // 컴파일 에러

 


 

public

  • public 접근 제어자 사용 시 클래스 멤버는 외부로 공개되며, 해당 객체는 어디서나 접근할 수 있습니다.
  • 자바 객체는 private 멤버 접근을 위해서는 반드시 public 메서드를 경유해야 합니다.
  • public이 붙은 외부 클래스는 하나의 소스 코드 파일 당 하나만 있을 수 있습니다.
    • 소스 코드 파일명과 public이 붙은 외부 클래스 파일명은 동일해야 합니다.
    • main 메소드는 public 클래스 내부에 위치해야 합니다. (main으로 접근하지 못하는 클래스가 존재하면 안 됨)

 

ClassP2.java (Package2)

package JavaPrctice.Package2;

public class ClassP2 {
//  4. public class의 이름은 java 파일과 동일
    public void publicMethod() {
        System.out.println("I am public");
    }
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod(); // 1. 동일 클래스, 2. 같은 패키지에서의 접근 허용
        // out : "i am public"
    }
}

public class Hello { 
// error : 3. 한 개의 자바 파일에 두개의 public class는 불가능
	private int a;
}

구현을 위해 ClassP2라는 public class를 만들었습니다.

 

위 구현 예제를 통해 public class의 성질 4가지를 알 수 있습니다. 

  1. 동일 클래스 멤버에서의 접근 허용
  2. 동일 패키지 멤버에서의 접근 허용
  3. 한 개의 자바 파일에 두 개의 public 클래스는 불가능
  4. public class의 이름은 java 파일과 동일하게 설정

 

3, 4를 제한하는 이유?
자바 파일 안에서 여러 개의 불특정 public class가 선언되는 것을 제한하는 이유는
어느 클래스가 어느 파일에 있는지 탐색이 매우 힘들어지기 때문입니다.
그래서 java 자체에서 개발 규약의 형태로 제한하고 있습니다.

 

이번에는 public의 외부 패키지에서의 접근, 자식 클래스에서의 접근 가능 여부 확인을 위해 

외부 패키지에서 다른 자바 파일을 아래와 같이 구현하겠습니다.

 

 

ClassP1.java (Package1)

package JavaPractice.Package1;
import JavaPractice.Package2.ClassP2;

public class ClassP1 extends ClassP2{
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod(); // 1. 외부 패키지에서의 접근 허용
        // out : i am public

        ClassP2 instance2 = new ClassP2(); 
        instance2.publicMethod(); // 2. (외부 패키지) 자식 클래스에서의 접근 허용
        // out : i am public
    }
}
  1. 외부 패키지에서의 접근 허용 확인을 위해 위에서 작성한 ClassP2를 현재 ClassP1.java에 import 하였습니다.
  2. 자식 클래스에서의 접근 허용 확인을 위해 ClassP1을 ClassP2를 상속받도록 하였습니다. 

 

결론 :  public 접근 제어자 멤버는 외부로 공개되어 어디에서나 멤버로 접근이 가능합니다.

구     분 해당 클래스 같은 패키지 내 클래스 자식 클래스 다른 패키지의 클래스
public  O O O O

 


 

protected

protected 멤버에 접근할 수 있는 영역은 아래와 같습니다.

  • protected 멤버를 선언한 해당 클래스의 멤버
  • protected 멤버가 선언한 클래스가 속한 패키지의 멤버
  • protected 멤버를 선언한 클래스의 자식 클래스의 멤버
  • 외부 패키지에서의 접근은 불가능 (단, 외부 클래스가 protected 멤버를 선언한 클래스의 자식 클래스인 경우 가능)

 

확인을 위해 이전에 구현한 ClassP2를 아래와 같이 수정했습니다.

ClassP2.java (Package2)

package JavaPrctice.Package2;

// (추가) 동일 패키지의 자식 클래스에서의 접근 허용 확인
class CLassP2Son extends ClassP2{}

public class ClassP2 {
    public void publicMethod() {
        System.out.println("I am public");
    } 
    // (추가) protectedMethod 선언
    protected void protectedMethod() {
        System.out.println("i am protected");
    }
    
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod();
        // out : "i am public"
        instance1.protectedMethod(); // (추가) 1. 동일 클래스에서 접근 허용, 2. 동일 패키지에서 접근 허용
        // out : "i am protected" 
        
        ClassP2Son instance2 = new ClassP2Son();
        instance2.publicMethod();
        // out : "i am public"
        instance2.protectedMethod(); // (추가) 3. 동일 패키지의 자식 클래스에서 접근 허용
        // out : "i am protected" 
    }
}

// public class Hello는 제거

위 구현 예제를 통해 public과 동일한 protected 접근 제어자의 특성 3가지를 확인했습니다. 

  1. 동일 클래스에서의 접근을 허용
  2. 동일 패키지에서의 접근을 허용
  3. 동일 패키지의 자식 클래스에서의 접근을 허용

 

그렇다면 외부 패키지에서의 접근 허용 확인을 위해 ClassP1을 수정하겠습니다.

ClassP1.java (Package1)

package JavaPractice.Package1;
import JavaPractice.Package2.ClassP2; // ClassP2 : 외부 패키지의 클래스

public class ClassP1 extends ClassP2{ // ClassP1 : 외부 패키지의 클래스를 상속받은 자식 클래스
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod(); 
         // out : i am public
        instance2.protectedMethod(); // (추가) 1. 외부 패키지에서의 접근 불가
         // error : protected 멤버는 외부 패키지로부터의 접근 불가

        ClassP2 instance2 = new ClassP2(); 
        instance2.publicMethod(); 
        // out : i am public
        instance2.protectedMethod(); // (추가) 2. 외부 패키지 출신 자식 클래스에서의 접근은 허용
        // out : i am protected
        
        ClassP2SonOuter instance3 = new ClassP2SonOuter();
        instance3.protectedMethod(); // (추가) ★ 외부 패키지인 자식 클래스에서 접근 불가 (default)
        // error
    }
}

class ClassP2SonOuter extends ClassP2 {} 
// ClassP2SonOuter : 외부 패키지의 클래스를 상속 받은 자식 클래스(default)
// 하지만 default로 선언되었기 때문에 접근 불가
// default 멤버는 default 멤버 -> 외부 패키지, 외부 패키지 -> default 멤버로 접근 불가

앞서 작성한 ClassP2와 ClassP1의 객체인 instance1과 instance2로 protectedMethod를 호출해 보았습니다.

  1. ClassP2의 인스턴스는 외부 패키지에서 호출하였으므로 protectedMethod를 호출 시 컴파일 에러를 발생시킵니다.
  2. ClassP1의 인스턴스는 외부 패키지라도, ClassP2의 자식 클래스이므로 protectedMethod를 정상적으로 호출합니다.

 

 

 

Default 멤버의 외부 자식 클래스로 protected 메서드 호출이 제한되는 이유?

 

1. 외부 패키지의 클래스가 default 클래스에 접근하는 것은 불가능
 * 다른 패키지에 속한 클래스는 default 클래스에 직접 접근할 수 없습니다.
 * default 클래스는 해당 패키지 내부에서만 접근 가능합니다.

2. default 클래스가 외부 패키지의 클래스에 접근하는 것 또한 불가능
 * default 클래스 또한 다른 패키지에 속한 클래스에 직접 접근할 수 없습니다.
 * 외부 패키지의 클래스는 default 클래스를 사용할 수 없습니다.

★ 따라서 외부 패키지의 클래스를 상속받아 protected 멤버를 호출하고자 하면
  상속받는 자식 클래스의 접근 제어자는 'public'이 되어야 합니다.

 

위 구현 예제를 통해 public과 동일한 protected 접근 제어자의 특성 2가지를 확인했습니다. 

  • 외부 패키지에서는 접근 불가
  • 단, 외부 패키지에 있는 protected 멤버가 속한 클래스의 자식 클래스는 접근 가능(자식 클래스가 public인 경우)

 

결론 :  protected 접근 제어자 멤버는 외부 패키지에서의 접근은 불가능합니다. (외부 패키지의 자식 클래스는 가능)

구     분 해당 클래스 같은 패키지 내 클래스 자식 클래스 다른 패키지의 클래스
protected  O O O X(자식 클래스는 O)

 


 

default

default는 접근 제어자를 별도 명시하지 않을 때, java에서 기본으로 지정하는 접근 제어입니다.

default 멤버가 접근할 수 있는 영역은 아래와 같습니다.

  • default 멤버를 선언한 해당 클래스의 멤버
  • default의 멤버가 선언한 클래스가 속한 패키지의 멤버

 

ClassP2를 수정하면서 다시 한번 확인해 보겠습니다.

ClassP2.java (Package2)

package JavaPrctice.Package2;

class CLassP2Son extends ClassP2{}

public class ClassP2 {
    public void publicMethod() {
        System.out.println("I am public");
    } 
    protected void protectedMethod() {
        System.out.println("i am protected");
    }
    // (추가) defaultMethod 선언
    protected void protectedMethod() {
        System.out.println("i am default");
    }
    
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod();
        // out : "i am public"
        instance1.protectedMethod(); // 
        // out : "i am protected" 
        instance1.defaultMethod(); // (추가) 1. 동일 클래스에서 접근 허용, 2. 동일 패키지에서 접근 허용
        // out : "i am default"
        
        ClassP2Son instance2 = new ClassP2Son();
        instance2.publicMethod();
        // out : "i am public"
        instance2.protectedMethod(); // 
        // out : "i am protected" 
        instance2.protectedMethod(); // (추가) 3. 동일 패키지의 자식 클래스에서 접근 허용
        // out : "i am protected" , 동일 패키지니까 동작
    }
}

위 구현 예제를 통해 public, protected과 동일한 default 접근 제어자의 특성 3가지를 확인했습니다. 

  1. 동일 클래스에서의 접근을 허용
  2. 동일 패키지에서의 접근을 허용
  3. 동일 패키지의 자식 클래스에서의 접근을 허용(동일 패키지이기 때문에 접근을 허용하는 것)

 

그렇다면 외부 패키지에서의 접근 확인을 위해 ClassP1을 수정하겠습니다.

ClassP1.java (Package1)

package JavaPractice.Package1;
import JavaPractice.Package2.ClassP2; // ClassP2 : 외부 패키지의 클래스

public class ClassP1 extends ClassP2{ // ClassP1 : 외부 패키지의 클래스를 상속받은 자식 클래스
    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod(); 
         // out : i am public
        instance2.protectedMethod(); 
         // error : protected 멤버는 외부 패키지로에서 접근 불가
        instance2.defaultMethod(); // (추가) 1. 외부 패키지에서 접근 불가
         // error : default 멤버는 외부 패키지에서 접근 불가

        ClassP2 instance2 = new ClassP2(); 
        instance2.publicMethod(); 
        // out : i am public
        instance2.protectedMethod();
        // out : i am protected
        instance2.defaultMethod(); // (추가) 2.외부 패키지 출신 자식 클래스도 접근 불가
        // error : default 멤버는 외부 패키지에서 접근 불가
    }
}

// ... 이하는 삭제

앞선 protected에서의 구현과 동일하게 Package1의 defaultMethod를 호출했지만 둘 다 컴파일 에러가 발생합니다.

 

결론:  default 멤버는 외부 패키지와의 접근을 제한하며, 같은 패키지에 속하는 멤버에서만 접근할 수 있습니다.

구     분 해당 클래스 같은 패키지 내 클래스 자식 클래스 다른 패키지의 클래스
default  O O X X

 


 

private

  • private의 멤버는 외부에 공개되지 않으며, 해당 클래스를 통해서만 접근이 가능합니다.
  • public 멤버 내 선언된 private의 경우에만 public을 통해서 해당 객체의 private 멤버에 접근할 수 있습니다.

 

내부 패키지에서의 private 접근 제어 동작 확인을 위해 ClassP2만 수정하여 확인하겠습니다.

ClassP2.java (Package2)

package JavaPractice.Package2;

class ClassP2Son extends ClassP2 { }

public class ClassP2 { // public 멤버에서만 private 멤버 접근 가능
    public void publicMethod() {
        System.out.println("I am public");
    }

    protected void protectedMethod() {
        System.out.println("i am protected");
    }

    void defaultMethod() {
        System.out.println("i am default");
    }

    private void privateMethod() { // (추가)
        System.out.println("i am private"); 
    }

    public static void main(String[] args) {
        ClassP2 instance1 = new ClassP2();
        instance1.publicMethod();
        // out : "i am public"
        instance1.protectedMethod();
        // out : "i am protected"
        instance1.defaultMethod();
        // out : "i am default"
        instance1.privateMethod(); // (추가) 1. 같은 클래스에서 private 멤버 접근 가능
        // out : "i am private"

        ClassP2Son instance2 = new ClassP2Son();
        instance2.publicMethod();
        // out : "i am public"
        instance2.protectedMethod();
        // out : "i am protected"
        instance2.defaultMethod();
        // out : "i am default"
        instance2.privateMethod(); // (추가) 2. 자식 클래스라도 private으로 접근 불가
        // error
        
        efaultClass defaultClass = new DefaultClass(); // (추가)
        System.out.println(defaultClass.privateString); // (추가) 3. public을 통한 private 접근이 아님
        // error
    }
}

class DefaultClass { // (추가) public 멤버가 아닌 곳에서 private이 선언된 경우
    private String privateString = "i am private in default class";
}

private 멤버인 privatemethod를 추가했습니다.

  1. 해당 private를 멤버로 필드로 가지고 있는 ClassP2의 객체는 private 멤버로 접근이 가능합니다.
  2. 그러나 그런 ClassP2를 상속받은 자식 클래스 ClassP2 Son의 객체는 private 멤버로 접근이 불가능합니다.
  3. 추가로, 외부에서 선언된 default 클래스 내부의 private 멤버 또한 private 멤버의 접근이 불가능합니다.

 

그렇다면 defaultClass의 private 멤버인 privateString으로 "접근이 가능하게끔" public을 활용해 클래스를 수정해 봅시다.

ClassP2.java (Package2)

package JavaPractice.Package2;

class ClassP2Son extends ClassP2 { }
public class ClassP2 {


		// ... 생략 ... 


        DefaultClass defaultClass = new DefaultClass();
        System.out.println(defaultClass.privateString); // public을 통한 private 접근이 아님
        // error
        System.out.println(defaultClass.getPrivateString()); // (추가) public 메소드를 통한 접근
        // out : "i am private in default class"
    }
}

class DefaultClass { // public 멤버가 아닌 곳에서 private이 선언된 경우
    private String privateString = "i am private in default class";
    public String getPrivateString(){ // (추가) public 메소드를 통한 접근
        return this.privateString;
    }
}

위와 같이 public 메서드를 통해 클래스의 private 멤버로 접근할 수 있게끔 수정하였습니다.

 

 

결론:  private 멤버는 선언 클래스 제외 어떤 외부에서도 접근 불가능, 접근 시 public 접근 제어자를 활용해야 합니다.

구     분 해당 클래스 같은 패키지 내 클래스 자식 클래스 다른 패키지의 클래스
public  O X X X

 

이러한 private 멤버는 싱글톤 패턴의 개발 기법에 많이 활용됩니다. (추후 포스팅에서 다루겠습니다.)

* 싱글톤 패턴:  인스턴스가 프로그램에서 하나만 생성되고, 어디서든 인스턴스에 접근할 수 있도록 하는 패턴

 

 

다음 시간에는 기타 제어자에 대해 학습해 보겠습니다.

 


# 작성 코드

https://github.com/Bisi3asi/Bisi3asi/tree/master/Java/TILjava/AccessModifierTest