1. 소개
Java에서도, 현실과 마찬가지로 모든 것이 모두에게 항상 공개될 필요는 없습니다. 집을 떠올려 보세요. 이웃이 여러분의 침실을 돌아다니길 원치 않겠죠? 프로그램에서도 어떤 변수나 메서드에 ‘문을 닫아’ 외부에서 접근하지 못하도록 하고 싶을 때가 있습니다.
이를 위해 Java에는 접근 제한자라는 특별한 키워드가 있습니다. 특정 변수나 메서드를 어디에서 사용할 수 있는지를 지정합니다. 예를 들어 public 또는 private 등이 있습니다.
주요 접근 제한자
| 제한자 | 가시 범위 |
|---|---|
|
클래스가 보이는 모든 곳(다른 패키지와 다른 파일에서도) |
|
동일한 클래스 내부에서만 |
|
동일한 패키지 내부에서만(즉, 같은 디렉터리의 클래스들 내부) |
다른 제한자들도 있지만, 상속을 다룰 때 이후에 이야기하겠습니다.
실전 예시
public class User
{
public String name; // 모두에게 보임
private int age; // User 클래스 내부에서만 보임
public void sayHello()
{
System.out.println("안녕하세요, 제 이름은 " + name);
}
private void secretMethod()
{
System.out.println("이것은 비밀 메서드입니다!");
}
}
설명:
- name — User 클래스를 사용할 수 있는 모든 곳에서 보입니다.
- age — User 클래스 내부에서만 보입니다.
- sayHello() — public 메서드로, 다른 어떤 클래스에서도 호출할 수 있습니다.
- secretMethod() — private 메서드로, 외부에서 호출할 수 없습니다.
실무에서는 어떻게 동작하나요?
은행 계좌를 나타내는 클래스를 생각해 봅시다. 누구나 잔액을 직접 바꿀 수 있도록 두고 싶지는 않겠죠! 그래서 잔액 변수는 private로 두고, 이를 다루는 전용 메서드를 제공합니다.
왜 모든 것을 public으로 만들면 안 될까요?
“그냥 다 public으로 해버리자, 편하게!”라는 유혹이 생길 수 있습니다. 하지만 그 길은 혼란으로 이어집니다. 누구나 여러분의 잔액, 사용자 이름을 바꾸거나 내부에서만 써야 할 메서드를 호출할 수도 있습니다. 규모가 큰 프로그램에서는 이런 일이 오류와 원인불명 ‘마법 같은’ 버그로 이어집니다.
Java 개발자의 황금률:
처음에는 모두를 private으로 두고, 정말 필요한 것만 외부에 공개하세요.
2. 변수의 유효 범위(스코프)
유효 범위는 변수가 ‘존재’하고 사용할 수 있는 코드의 영역입니다. 이 영역을 벗어나면 변수는 ‘사라지며’ — 마치 처음부터 없었던 것처럼 동작합니다.
Java에서는 유효 범위에 따라 몇 가지 종류의 변수가 있습니다:
- 지역 변수 — 메서드나 코드 블록 { ... } 내부에서 선언됩니다.
- 메서드 매개변수 — 메서드의 괄호 안에서 선언됩니다.
- 클래스 필드 — 클래스 내부이되 메서드 바깥에서 선언됩니다.
변수가 { 블록 } 내부에서 선언되면,
그 블록 내부에서만 보이며
블록 밖에서는 사용할 수 없습니다.
지역 변수
지역 변수는 선언된 메서드 또는 블록 내부에서만 존재합니다.
void printSum(int a, int b)
{
int sum = a + b; // 지역 변수
System.out.println(sum);
}
// 여기서는 sum이 더 이상 존재하지 않음!
메서드 밖에서 sum에 접근하려 하면 컴파일 오류가 발생합니다: “변수 sum을(를) 찾을 수 없습니다”.
메서드 매개변수
매개변수도 변수이지만, 수명은 메서드 내부로 제한됩니다.
void greet(String name)
{
System.out.println("안녕하세요, " + name);
}
// 여기서는 name이 더 이상 존재하지 않음!
클래스 필드(멤버 변수)
클래스 필드는 클래스 내부이되 메서드 바깥에서 선언됩니다. 이 클래스의 모든 메서드에서 볼 수 있습니다.
public class Counter
{
private int count = 0; // 클래스 필드
public void increment()
{
count++; // 필드를 사용할 수 있음
}
public int getCount()
{
return count; // 역시 필드를 사용할 수 있음
}
}
3. 변수 섀도잉(Shadowing)
섀도잉(shadowing)은 한 유효 범위 안에서 외부 유효 범위와 같은 이름의 변수(또는 매개변수)를 선언하는 상황을 말합니다. 이 블록 내부에서는 ‘새로운’ 이름이 기존 이름을 가리게 되어, 외부 값에는 직접 접근할 수 없습니다.
섀도잉 예시:
class ShadowDemo
{
int value = 10; // 클래스 필드
void printValue()
{
System.out.println(value); // 10 — 클래스 필드를 출력
int value = 5; // 지역 변수가 클래스 필드를 가린다
System.out.println(value); // 10이 아니라 5를 출력
}
}
이 예시에서 int value = 5;는 동일한 이름의 새로운 지역 변수를 선언하며, 이는 클래스 필드보다 우선합니다. printValue() 메서드 내부에서 value에 접근하면 클래스 필드가 아니라 지역 변수가 사용됩니다.
그래도 정적 클래스 필드에 접근해야 한다면, 클래스 이름을 접두사로 사용하세요:
class ShadowDemo
{
static int value = 10; // 정적 클래스 필드
void printValue()
{
System.out.println(value); // 10 — 클래스 필드
int value = 5;
System.out.println(value); // 5 — 지역 변수
System.out.println(ShadowDemo.value); // 10 — 정적 클래스 필드, 'ShadowDemo'를 통해 접근
}
}
비정적 클래스 필드에 접근해야 한다면 this 키워드를 사용합니다. 이는 현재 객체 인스턴스를 가리킵니다.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — 클래스 필드
int value = 5;
System.out.println(value); // 5 — 지역 변수
System.out.println(this.value); // 10 — 클래스 필드, 'this'를 통해 접근
}
}
4. 실습: 접근 제한자와 스코프
학습용 애플리케이션을 확장해 봅시다. 간단한 학생 관리 시스템입니다. 서로 다른 접근 제한자를 가진 필드와 메서드를 만들어 보겠습니다.
예시: Student 클래스
public class Student
{
public String name; // 학생 이름(모두에게 보임)
private int age; // 나이(클래스 내부에서만 보임)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("안녕하세요, 제 이름은 " + name);
}
private void printSecret()
{
System.out.println("제 나이: " + age);
}
public void revealSecret()
{
printSecret(); // 클래스 내부에서는 private 메서드를 호출할 수 있음
}
}
Student 클래스 사용
public class Main
{
public static void main(String[] args)
{
Student s = new Student("Vasya", 20);
s.sayHello(); // OK: public 메서드
s.revealSecret(); // OK: 내부에서 private을 호출하는 public 메서드
s.age = 30; // 오류! 필드 age는 private임
s.printSecret(); // 오류! printSecret 메서드는 private임
}
}
5. 접근 제한자와 스코프에서 흔한 실수
오류 1번: 전부를 public으로 — 혼란의 시작!
모든 필드와 메서드를 public으로 만들어 버리면, 누가 어떻게 데이터를 바꾸는지 통제력을 잃게 됩니다. 필드는 private으로 두고, 꼭 필요한 것만 외부에 공개하세요.
오류 2번: 변수의 유효 범위 밖에서 변수를 사용하려는 시도.
예를 들어 메서드 내부에서 변수를 선언해 놓고, 그 밖에서 접근하려고 하면 컴파일 오류가 납니다. 지역 변수는 자신이 속한 블록 { ... }에서만 살아 있습니다.
오류 3번: 이름 충돌(변수 섀도잉).
메서드 안에서 클래스 필드와 같은 이름의 변수를 선언하면, 어떤 변수를 쓰는지 혼동하기 쉽습니다. this를 사용해 클래스 필드를 명시적으로 가리키세요: this.value.
오류 4번: 다른 클래스에서 private 메서드나 필드에 접근하려는 시도.
메서드나 필드가 private이면 동일한 클래스 내부에서만 접근할 수 있습니다. 외부에서 접근하려 하면 컴파일 오류가 발생합니다.
오류 5번: 루프나 블록의 스코프를 잊음.
루프나 임의의 블록 { } 내부에서 선언된 변수는 해당 블록 내부에서만 존재합니다. 블록 밖에서는 존재하지 않습니다.
GO TO FULL VERSION