아래의 내용은 java_grammer 레파지토리 C02MethodClass 디렉터리에 저장되어있는 내용을 정리하였다.
변수는 선언된 위치(문맥)에 따라 유효 범위가 달라진다.
보통 자바에서는 크게 다음 3가지 범위로 나눌 수 있다.
이를 코드로 살펴보면 이해가 빠르다.
public class C05Scope {
static int v1 = 10; // 클래스 변수 (프로그램 전체 범위)
int v2 = 20; // 객체 변수 (객체 내부에서만 접근 가능)
public static void main(String[] args) {
int v3 = 10; // 지역 변수 (해당 메서드 내에서만 유효)
}
}
static)는 클래스 전역(어디서든)에서 접근 가능하다. 즉, 변수의 생명주기는 선언된 위치와 메모리에 할당되는 영역에 의해 결정된다.
자바에서 기본형 자료형(primitive types)은 모두 값에 의한 호출 방식으로 전달된다.
즉, 변수의 값만 복사해서 전달되며, 원본 값에는 영향을 주지 않는다.
public static void main(String[] args) {
int v3 = 10;
scope1(v3);
System.out.println(v3); // 10 출력
}
public static void scope1(int v3) {
v3 = 30;
System.out.println(v3); // 30 출력
}
출력 결과를 보면 다음과 같다.
30
10
scope1() 메서드 내부에서 v3를 30으로 바꾸었지만,
main 메서드에 있는 v3는 그대로 10인 이유는, 값(10)의 복사본만 전달되었기 때문이다.
즉, 서로 다른 메모리 공간을 사용하고 있으며,
scope1의 파라미터가 수정되어도 원본에는 아무런 영향이 없다.
📍 main() 호출 전
Stack: main() { int v3 = 10 }
📍 scope1(v3) 호출 시
Stack:
┌─────────────┐
│ scope1() │ { int v3 = 10 ← 복사된 값 }
├─────────────┤
│ main() │ { int v3 = 10 ← 원본 }
└─────────────┘
📍 scope1() 내부에서 v3 = 30 실행
Stack:
┌─────────────┐
│ scope1() │ { int v3 = 30 ← 복사본만 변경 }
├─────────────┤
│ main() │ { int v3 = 10 ← 원본 그대로 }
└─────────────┘
📍 scope1() 종료 후
Stack: main() { int v3 = 10 }
반면, 참조형 변수(reference type)는 객체의 실제 주소값(참조값)이 전달된다.
즉, 복사된 것이 아니라 원본 메모리 주소를 그대로 공유하게 된다.
C04Person2 p1 = new C04Person2();
p1.setName("kim");
p1.setEmail("kim@naver.com");
p1.setAge(15);
scope2(p1);
System.out.println(p1.returnPersonInfo());
scope2() 메서드 내부에서는 이렇게 정의되어 있다.
public static void scope2(C04Person2 person) {
person.setName("lee");
System.out.println(person.returnPersonInfo()); // lee
}
실행 결과는 다음과 같다.
이름은 lee, 이메일은 kim@naver.com, 나이는 15세 입니다.
이름은 lee, 이메일은 kim@naver.com, 나이는 15세 입니다.
메서드 내부에서 person.setName("lee")로 값을 변경하자,
원본 p1 객체의 name도 함께 바뀌었다.
이유는 메서드 매개변수 person이 p1이 참조하고 있는 같은 힙(Heap) 메모리 주소를 가리키고 있기 때문이다.
📍 main()에서 객체 생성
Stack: main() { C04Person2 p1 → 0x1000 }
Heap: [0x1000] { name="kim", email, age=15 }
📍 scope2(p1) 호출 시
Stack:
┌─────────────┐
│ scope2() │ { C04Person2 person → 0x1000 ← 같은 주소 }
├─────────────┤
│ main() │ { C04Person2 p1 → 0x1000 ← 같은 주소 }
└─────────────┘
Heap: [0x1000] { name="kim", email, age=15 }
📍 scope2() 내부에서 person.setName("lee")
Stack: 동일
Heap: [0x1000] { name="lee", email, age=15 ← 원본 변경 }
📍 scope2() 종료 후
Stack: main() { C04Person2 p1 → 0x1000 }
Heap: [0x1000] { name="lee", email, age=15 }
| 구분 | 전달 방식 | 전달되는 값 | 원본 데이터 변경 여부 |
|---|---|---|---|
| Call by Value | 값 복사 | 변수의 값(예: 10) | 변경되지 않음 |
| Call by Reference | 참조 복사 | 객체의 주소값 | 변경됨 (같은 메모리 참조) |
자바의 모든 객체형(ex: String, 배열, 클래스)은 사실상 call by reference처럼 동작한다.
단, 자바에서는 모든 것이 “값에 의한 전달”이다.
즉, 참조형 변수의 “주소값”을 값으로 전달하고 있을 뿐이다.
이 부분은 면접에서도 자주 물어보는 개념이므로 정확히 이해해 두면 좋다.
static)는 프로그램 전역에서 공유된다.