[Java] 지역 클래스
package nested.outer;
public class LocalOuterV1 {
private int outInstanceVar = 3;
//1번째 메서드
public void process(int paraVar) {
//1번째 메서드의 지역변수인 localVar
int localVar = 1;
class LocalPrinter {
int value = 0 ;
public void printData() {
System.out.println("value: " + value);
System.out.println("outInstanceVar: " + localVar);
//매개변수에도 접근 가능하다
System.out.println( "paraVar : "+ paraVar);
System.out.println("outInstanceVar : " + outInstanceVar);
}
}
LocalPrinter localPrinter = new LocalPrinter();
localPrinter.printData();
}
public static void main(String[] args) {
LocalOuterV1 localOuterV1 = new LocalOuterV1();
localOuterV1.process(1);
}
}
value: 0
outInstanceVar: 1
paraVar : 1
outInstanceVar : 3
다 접근이 가능하다.
지역 클래스의 접근 범위
- 자신의 인스턴스의 변수인 맴버변수인 outInstanceVar 에 접근할 수 있다
- 매개 변수인 paraVar 에 접근할 수있다.
- 지역 변수인 localVar 에 접근 할 수 있다 등등
지역 클래스는 지역 변수 처럼 접근 제어자를 사용할 수 없다.
이는 지역 변수인 localVar을 private int localVar 을 사용할 수 없다.
지역 클래스 - 지역 변수 캡처1
변수의 생명 주기
- 클래스 변수 : 프로그램 종료까지, 생명주기가 가장 길다(메서드 영역)
클래스(static 변수)는 메서드 영역에 존재하고, 자바가 클래스 정보를 읽어 들어오는 순간부터 프로그램 종료까지 존재한다. - 인스턴스 변수 : 인스턴스의 생존 기간(힙 영역)
인스턴스 변수는 본인이 소속된 인스턴스가 GC되기 전까지 존재한다. 생존 주기가 긴 편이다 - 지역 변수: 메서드 호출이 끝나면 사라진다 (스택 영역)
지역변수는 스택 영역의 스택 프레임안에 존재한다. 따라서 메서드 호출 되면 생성되고, 메서드 호출이 종료되면 스택 프레임이 제거되면서 그 안에 있는 지역 변수도 모두 제거된다. 생존 주기가 아주 짧다. 참고로 매개 변수도 지역 변수의 한 종류이다.
메모리 영역설명생명 주기저장되는 데이터
메모리 영역 | 설명 | 생명 주기 | 저장되는 데이터 |
메서드(Method) 영역 (클래스 영역) | 클래스, 메서드 정보 | JVM 종료 시까지 유지 (가장 길다) | 클래스(Static 변수), 메서드 코드, 인터페이스 |
힙(Heap) 영역 | 객체가 저장되는 영역 | 객체가 참조될 때까지 유지 (GC 실행 시 제거 가능) |
new로 생성된 객체, 인스턴스 변수 |
스택(Stack) 영역 | 메서드 실행 시 생성되는 공간 | 메서드 실행 중에만 유지 (가장 짧다) | 지역 변수, 매개변수, return 주소 |
PC 레지스터 & 네이티브 스택 | 현재 실행 중인 명령어 | 쓰레드 실행 중 유지 | 현재 실행 중인 명령어 주소 |
즉, 인터페이스 Printer 타입의 참조 변수 printer가 LocalPrinter 객체를 가리키는 것
부분 | 의미 |
Printer | 인터페이스 (참조 변수 타입) |
printer | 참조 변수 (객체를 가리킴) |
new LocalPrinter(); | LocalPrinter의 인스턴스 생성 |
지역클래스는 메서드안에서 생성되는 클래스를 지역클래스라고 하는데,
public class OuterClass {
void myMethod() {
class LocalClass { // 지역 클래스 선언
void showMessage() {
System.out.println("지역 클래스에서 실행됨!");
}
}
LocalClass local = new LocalClass();
local.showMessage();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.myMethod(); // "지역 클래스에서 실행됨!" 출력
}
}
특징으로는
- 메서드 내부에서 정의되는 클래스로, 해당 메서드 안에서만 사용 가능.
- 지역 클래스는 static을 가질 수 없음 (static 멤버는 정의할 수 없음).
- 외부 클래스의 멤버에는 접근할 수 있지만, 지역 변수에 접근하려면 지역 변수가 final 또는 effectively final이어야 함.
여기서 지역 변수의 캡쳐라는 개념이 있다.
지역 클래스에서 메서드의 지역 변수를 사용할 경우, 해당 지역 변수는 final 또는 effectively final(사실상 final) 이어야 합니다.
왜 지역 변수는 final이어야 할까?
지역 클래스의 인스턴스는 메서드 실행이 끝난 후에도 계속 존재할 수 있기 때문입니다.
메서드 실행이 끝나면 지역 변수는 사라지지만, 지역 클래스 내부에서는 계속 사용될 수 있기 때문에 변수를 안전하게 유지하려면 final이어야 한다.
- 지역 변수는 스택영역에도 올라가고, 힙영역에는 인스턴스가 올라가면서 저장된 지역변수를 포함된 값이 있다.
만약 스택영역에는 원래 메서드에 호출되면서 저장되는 지역변수를 저장하는데, 지역클래스의 특성으로 힙 영역에 스택영역에서 복사한 지역변수를 인스턴스에 포함시킨다.
복사한 지역 변수를 포함해서 인스턴스 생성이 완료가 된다.
근데 그러면 캡쳐한 지역변수와 원래 스택 영역에 올라간 지역 변수는 어떻게 동기화가 되는 걸까?
이러한 변화는 문제때문에 final을 줘서 변경되지 않도록 한다.
잘못된 예시
public class Test {
void myMethod() {
int num = 10; // 지역 변수
class LocalClass {
void printNum() {
System.out.println(num); // ⚠ 컴파일 오류 발생 가능
}
}
num = 20; // ⚠ 지역 변수가 변경되면 effectively final이 아니므로 오류 발생
LocalClass local = new LocalClass();
local.printNum();
}
public static void main(String[] args) {
Test test = new Test();
test.myMethod();
}
}
올바른 예시
public class Test {
void myMethod() {
final int num = 10; // final 사용 (또는 사실상 final)
class LocalClass {
void printNum() {
System.out.println(num); // 정상 실행
}
}
LocalClass local = new LocalClass();
local.printNum();
}
public static void main(String[] args) {
Test test = new Test();
test.myMethod(); // "10" 출력
}
}
effectively final이란?
Java 8 이후부터는 변수에 final을 명시적으로 선언하지 않아도, 값이 변경되지 않으면 자동으로 final로 간주.
즉, 한 번만 할당되고 변경되지 않으면 "effectively final"로 취급
예제
public class Test {
void myMethod() {
int num = 10; // final이 아니지만 변경되지 않으므로 effectively final
class LocalClass {
void printNum() {
System.out.println(num); // 정상 실행
}
}
LocalClass local = new LocalClass();
local.printNum();
}
public static void main(String[] args) {
Test test = new Test();
test.myMethod(); // "10" 출력
}
}
num은 final 키워드가 없지만 값을 변경하지 않음 → effectively final로 간주되어 정상 동작!
지역 변수 캡처 정리
- 지역 클래스에서 메서드의 지역 변수를 사용하려면 해당 변수는 final 또는 effectively final이어야 함.
- 지역 클래스는 메서드 실행이 끝난 후에도 객체로 남아 있을 수 있기 때문에, 변수가 변경되면 안 됨.
- effectively final은 Java 8 이후 도입된 개념으로, 변수가 변경되지 않으면 자동으로 final로 취급됨.
🎯 핵심 요약
- 지역 클래스는 메서드 내부에서만 사용.
- 지역 클래스가 지역 변수를 사용할 경우, 해당 변수는 변경될 수 없음.
- Java 8부터는 effectively final 개념이 도입되어, 변경되지 않는 변수는 자동으로 final로 취급.