[TIL] Java 접근 제한자(Access Modifier)

revo·2026년 4월 14일

자바

목록 보기
22/30
post-thumbnail

접근 제한자란

멤버(필드, 메서드, 생성자) 또는 클래스에 대한 접근 범위를 컴파일 타임에 제한하는 키워드.

객체지향의 캡슐화(Encapsulation) 핵심은 "외부에서 직접 건드리면 안 되는 것을 막는 것"이다. 접근 제한자가 그 수단이다.


종류와 범위

제한자같은 클래스같은 패키지자식 클래스전체
public
protected
(default)
private

(default)는 키워드를 아무것도 붙이지 않은 상태로, package-private 이라고도 부른다.


하나씩 보자

public

public class Digimon {
    public String name;
}

어디서든 접근 가능하다. 제한이 없다.


private

public class Digimon {
    private int hp;

    public int getHp() { return hp; }
    public void setHp(int hp) { this.hp = hp; }
}

같은 클래스 내부에서만 접근 가능하다. 외부에서 digimon.hp 직접 접근은 불가능하다.

필드를 private으로 막고 getter/setter로 노출하는 것이 캡슐화의 기본 패턴이다.


default (package-private)

// com.digimon 패키지
class DigimonHelper {   // public 없음 → default
    void help() { }     // public 없음 → default
}

같은 패키지 내에서만 접근 가능하다. 다른 패키지에서는 클래스 자체가 보이지 않는다.

// com.other 패키지
import com.digimon.DigimonHelper; // 컴파일 에러 — 접근 불가

protected

default에서 자식 클래스(상속) 접근만 추가된 것이다.

// com.digimon 패키지
public class Digimon {
    protected int level;
}

// com.other 패키지
public class Agumon extends Digimon {
    public void printLevel() {
        System.out.println(level); // 가능 — 자식 클래스이므로
    }
}

// com.other 패키지
public class Other {
    public void test(Digimon d) {
        System.out.println(d.level); // 컴파일 에러 — 자식 클래스가 아님
    }
}

protected"자식 클래스면 패키지가 달라도 된다" 는 의미다.


protected의 핵심 — 가장 많이 헷갈리는 부분

상속 후 멤버 포함관계

AgumonDigimon을 상속받으면, levelAgumon 객체 안에 포함된다.

[ Agumon 인스턴스 ]
  └── level = 10   (Digimon에서 상속받아 내 것이 된 필드)

[ Digimon 인스턴스 ]
  └── level = 10   (Digimon 자신의 필드)

둘은 완전히 별개의 객체다.

자식 클래스 안에서 부모 인스턴스의 protected 멤버에 접근하면?

// com.other 패키지, Agumon 안에서
public class Agumon extends Digimon {
    public void test() {
        Digimon d = new Digimon();
        System.out.println(d.level); // 컴파일 에러
    }
}

Agumonlevel을 상속받았다는 건 Agumon 객체 안의 level에 접근할 수 있다는 것이지, Digimon 인스턴스의 level에 접근할 수 있다는 게 아니다.

com.other에서 Digimon 인스턴스의 protected 멤버를 직접 찍는 건, 상속과 무관하게 다른 패키지에서 외부 접근하는 것과 동일하게 취급된다.

protected는 "상속받은 내 것"에 접근하는 것이지, "부모 인스턴스의 것"에 접근하는 게 아니다.

올바른 접근 방법

public class Agumon extends Digimon {
    public void test() {
        System.out.println(level);       // 가능 — 자기 자신의 level
        System.out.println(this.level);  // 가능 — 명시적으로 자기 자신의 level

        Agumon a = new Agumon();
        System.out.println(a.level);     // 가능 — Agumon 타입이므로

        Digimon d = new Digimon();
        System.out.println(d.level);     // 불가 — 부모 인스턴스를 외부에서 직접 접근
    }
}

클래스 접근 권한과 멤버 접근 권한은 별개다. Digimonpublic 클래스라 인스턴스 생성은 가능하지만, protected 멤버에 대한 접근 권한은 별도로 적용된다.


클래스에 붙는 접근 제한자

top-level 클래스(파일 최상위에 선언된 클래스, 어떤 클래스 안에도 속해있지 않은 클래스) 에는 publicdefault만 쓸 수 있다.

public class Agumon { }    // 가능
class Agumon { }           // 가능 (default)
private class Agumon { }   // 컴파일 에러
protected class Agumon { } // 컴파일 에러

private/protected는 상위 구조 안에서의 관계를 정의하는 것인데, top-level 클래스는 상위 구조가 없어서 의미 자체가 성립하지 않는다.

단, 중첩 클래스(nested class) 는 4가지 전부 쓸 수 있다. 클래스 안에 있어서 상위 구조가 존재하기 때문이다.

public class Digimon {
    private int hp = 100;

    public class Inner {
        public void show() {
            System.out.println(hp); // 가능 — 같은 클래스 안이므로
        }
    }
}

필드 숨김(Field Hiding) — 오버라이딩과 혼동 주의

오버라이딩은 메서드에만 적용되는 개념이다. 필드에서 같은 이름을 선언하면 오버라이딩이 아니라 필드 숨김(Field Hiding) 이 발생한다.

public class Digimon {
    protected int level = 10;
}

public class Agumon extends Digimon {
    int level = 99; // 오버라이딩 X — 새로운 필드 선언
}

이 상태에서 Agumon 인스턴스 안에는 level이 두 개 존재한다.

[ Agumon 인스턴스 ]
  ├── level = 10  (Digimon에서 상속받은 것)
  └── level = 99  (Agumon이 새로 선언한 것)
Agumon a = new Agumon();
System.out.println(a.level);             // 99 — Agumon의 level
System.out.println(((Digimon)a).level);  // 10 — Digimon의 level
메서드필드
같은 이름 재선언오버라이딩필드 숨김(Field Hiding)
런타임 다형성 적용OX
부모 것 접근super.method()super.field

캡슐화 관점에서의 설계 원칙

가능한 한 가장 좁은 범위로 선언하고, 필요할 때만 넓혀라.

public class Digimon {
    private String name;
    private int level;

    public Digimon(String name, int level) {
        this.name = name;
        this.level = level;
    }

    public String getName() { return name; } // 읽기만 허용
    // setter 없음 → 불변 필드로 관리
}

setter를 만들지 않으면 외부에서 값을 바꿀 수 없다. 의도적인 설계다.


정리

제한자범위주요 용도
public전체API, 외부에 공개할 것
protected패키지 + 자식 클래스상속 계층에서 내부 공유
default같은 패키지패키지 내부 구현
private같은 클래스캡슐화, 내부 구현 은닉

0개의 댓글