| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 알고리즘공부
- 프로그래머스
- HashMap
- 자바프로그래밍
- 자바
- 자바기초
- 코딩공부
- 코딩테스트
- 예외처리
- 파이썬
- 코딩테스트팁
- 자료구조
- 개발자취업
- 메모리관리
- 클린코드
- 자바공부
- 정렬
- 개발자팁
- 멀티스레드
- 객체지향
- 자바개발
- JVM
- 코딩테스트준비
- 프로그래밍기초
- 백준
- 가비지컬렉션
- 알고리즘
- Java
- 코딩인터뷰
- 개발공부
- Today
- Total
코드 한 줄의 기록
Java 완전정복! 변수 스코프, final, 상수 패턴 한 번에 이해하기 본문
Java를 공부하다 보면 변수를 사용할 때 헷갈리는 부분들이 많죠? 특히 변수가 어디서부터 어디까지 사용 가능한지(스코프), final 키워드를 언제 써야 하는지, 그리고 상수를 만들 때 클래스를 쓸지 enum을 쓸지... 이런 고민들 한 번쯤은 해보셨을 거예요.
저도 처음 Java를 배울 때 이런 개념들이 정말 어려웠는데요. 그래서 이번 글에서는 제가 공부하면서 정리한 내용을 여러분과 함께 나누고 싶습니다. 하나씩 차근차근 알아가다 보면 분명히 도움이 될 거예요!
변수 스코프(Scope) - 변수의 생존 범위 이해하기
스코프란 무엇일까요?
스코프(Scope)는 변수나 메서드가 접근 가능한 유효 범위를 의미합니다. 쉽게 말해서 "이 변수가 어디서부터 어디까지 살아있는가?"를 정의하는 것이죠.
클래스 스코프 (Class Scope)
클래스 스코프는 클래스 내에 선언된 멤버 변수의 유효 범위를 말합니다. 이 변수들은 클래스 레벨에서 선언되며, 해당 클래스의 인스턴스가 생성될 때 함께 생성됩니다.
public class Car {
private String brand; // 인스턴스 변수
private static int count = 0; // 클래스 변수
public void setBrand(String brand) {
this.brand = brand; // 클래스 전체에서 접근 가능
}
public void printDetails() {
System.out.println("Brand: " + brand);
System.out.println("Total cars: " + count);
}
}메서드 스코프 (Method Scope)
메서드 내에서 선언된 변수들은 해당 메서드 내에서만 유효합니다. 메서드 실행이 끝나면 이 변수들은 메모리에서 사라지게 됩니다.
public void calculateScore() {
int totalScore = 0; // 메서드 스코프
int bonus = 10; // 메서드 스코프
totalScore = bonus * 5;
// totalScore와 bonus는 여기서만 사용 가능
}
// 여기서는 totalScore와 bonus에 접근 불가블록 스코프 (Block Scope)
중괄호({})로 둘러싸인 코드 블록 내에서만 유효한 변수의 범위입니다. if문, for문, while문 등에서 선언된 변수가 여기에 해당합니다.
public void processData() {
int mainValue = 100;
if (mainValue > 50) {
int tempValue = 20; // 블록 스코프
System.out.println(tempValue); // 정상 동작
}
// System.out.println(tempValue); // 컴파일 에러!
for (int i = 0; i < 5; i++) { // i는 for문 블록 내에서만 유효
int loopTemp = i * 2;
}
// System.out.println(i); // 컴파일 에러!
}변수 초기화 순서
Java에서 변수들은 정해진 순서에 따라 초기화됩니다.
클래스 변수 초기화 순서
- 기본값 할당
- 명시적 초기화
- 클래스 초기화 블록 실행
인스턴스 변수 초기화 순서
- 기본값 할당
- 명시적 초기화
- 인스턴스 초기화 블록 실행
- 생성자 실행
class InitExample {
static int classVar = 10; // 명시적 초기화
int instanceVar = 20; // 명시적 초기화
static { // 클래스 초기화 블록
classVar = 30;
}
{ // 인스턴스 초기화 블록
instanceVar = 40;
}
InitExample() { // 생성자
instanceVar = 50; // 최종값
}
}final 키워드 - 불변성의 핵심
final이 왜 중요한가요?
final 키워드는 "최종적인", "변경할 수 없는"이라는 의미를 가집니다. Java에서 불변성을 확보할 수 있도록 해주는 핵심 키워드죠.
변수에 사용되는 final
변수에 final을 붙이면 한 번 값이 할당되면 절대 변경할 수 없습니다.
public class FinalExample {
final int MAX_SCORE = 100; // 선언과 동시에 초기화
final String name; // 생성자에서 초기화 가능
public FinalExample(String name) {
this.name = name; // 생성자에서 한 번만 초기화
// this.name = "new name"; // 컴파일 에러!
}
public void testFinal() {
final int localVar = 10;
// localVar = 20; // 컴파일 에러!
final List list = new ArrayList<>();
list.add("item"); // 리스트 내용 변경은 가능
// list = new ArrayList<>(); // 참조 변경은 불가!
}
}
중요한 포인트: final이 붙은 참조형 변수는 참조 주소는 변경할 수 없지만, 객체 내부의 값은 변경 가능합니다.
메서드에 사용되는 final
final 메서드는 오버라이딩할 수 없습니다.
class Parent {
public final void finalMethod() {
System.out.println("오버라이딩 불가!");
}
}
class Child extends Parent {
// public void finalMethod() { } // 컴파일 에러!
}클래스에 사용되는 final
final 클래스는 상속할 수 없습니다. 대표적인 예로 String 클래스가 있습니다.
public final class FinalClass {
// 내용
}
// class ExtendedClass extends FinalClass { } // 컴파일 에러!상수 패턴 - 클래스 vs Enum 비교
static final을 이용한 상수 클래스
전통적으로 Java에서는 static final을 사용해서 상수를 정의했습니다.
public class GameConstants {
public static final String GAME_TITLE = "슈퍼 게임";
public static final int MAX_PLAYER = 4;
public static final int MIN_PLAYER = 2;
// 상태 상수들
public static final int STATUS_READY = 1;
public static final int STATUS_PLAYING = 2;
public static final int STATUS_FINISHED = 3;
}
static final을 사용하는 이유
- 메모리 효율성: 인스턴스마다 새로운 메모리를 할당하지 않고 하나의 메모리 공간만 사용합니다
- 공유성: 모든 곳에서 하나의 값을 일관되게 사용할 수 있습니다
Enum을 이용한 상수 정의
Java 5부터 도입된 enum은 열거형을 나타내는 특별한 클래스입니다.
public enum GameStatus {
READY("준비"),
PLAYING("게임 중"),
FINISHED("완료");
private final String description;
GameStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
// 게임 상태 체크 메서드
public boolean canStart() {
return this == READY;
}
}상수 클래스 vs Enum 비교
| 항목 | 상수 클래스 | Enum |
| 타입 안정성 | 약함 (int값 혼용 가능) | 강함 (컴파일 타임 검사) |
| 네임스페이스 | 접두사 필요 | 자동 제공 |
| 메모리 사용 | 적음 | 상대적으로 많음 |
| 기능 확장 | 제한적 | 메서드/필드 추가 가능 |
| Switch 문 | 사용 가능 | 사용 가능 (더 안전) |
| IDE 지원 | 보통 | 우수 (자동완성, 오타검증) |
| 유지보수 | 어려움 | 용이 |
Enum의 장점들
1. 타입 안정성
// 상수 클래스 - 실수 가능
public void processStatus(int status) {
if (status == GameConstants.STATUS_READY) {
// 실수로 다른 int값을 넣어도 컴파일됨
}
}
// Enum - 타입 안전
public void processStatus(GameStatus status) {
if (status == GameStatus.READY) {
// GameStatus가 아닌 값은 컴파일 에러
}
}
2. 싱글톤 보장
enum의 각 상수는 JVM 내에서 단 하나의 인스턴스만 존재합니다. 그래서 == 비교가 가능하고 equals()보다 안전합니다.
GameStatus status1 = GameStatus.READY;
GameStatus status2 = GameStatus.READY;
System.out.println(status1 == status2); // true
System.out.println(status1.equals(status2)); // true (하지만 NPE 위험)
3. Switch 문에서의 활용
public String getStatusMessage(GameStatus status) {
switch (status) {
case READY:
return "게임 준비 중입니다";
case PLAYING:
return "게임이 진행 중입니다";
case FINISHED:
return "게임이 완료되었습니다";
default:
throw new IllegalArgumentException("알 수 없는 상태: " + status);
}
}Enum의 단점들
1. 런타임 변경 불가
- enum은 컴파일 타임에 고정되므로 런타임에 값을 추가하거나 변경할 수 없습니다.
2. 메모리 사용량 증가
- enum은 일종의 객체이므로 단순 상수보다 메모리를 더 많이 사용합니다.
3. 상속 불가
- enum은 이미 Enum 클래스를 상속받고 있어서 다른 클래스를 상속할 수 없습니다.
언제 어떤 것을 사용할까요?
상수 클래스를 사용하는 경우
- 단순한 기본값들 (문자열, 숫자)
- 메모리 사용량이 중요한 경우
- 값이 자주 변경될 가능성이 있는 경우
Enum을 사용하는 경우
- 서로 관련된 상수들의 그룹
- 타입 안정성이 중요한 경우
- 상수에 추가적인 기능(메서드)이 필요한 경우
- Switch 문에서 사용할 경우
// 이런 경우엔 Enum이 더 좋아요
public enum UserRole {
ADMIN("관리자", 100),
MODERATOR("운영자", 50),
USER("일반사용자", 10);
private final String displayName;
private final int priority;
UserRole(String displayName, int priority) {
this.displayName = displayName;
this.priority = priority;
}
public boolean hasHigherPriorityThan(UserRole other) {
return this.priority > other.priority;
}
public String getDisplayName() {
return displayName;
}
}실전 활용 예제
마지막으로 이 모든 개념들을 종합한 실제 활용 예제를 보여드릴게요.
public class GameManager {
// 게임 설정 상수들
private static final int MAX_PLAYERS = 4;
private static final String GAME_VERSION = "1.0.0";
// 게임 상태 (Enum 활용)
private GameStatus currentStatus;
private final List players; // final로 리스트 참조 고정
public GameManager() {
this.currentStatus = GameStatus.READY;
this.players = new ArrayList<>(); // 내용은 변경 가능
}
public void startGame() {
// 블록 스코프 활용
if (currentStatus.canStart()) {
int playerCount = players.size(); // 메서드 스코프 변수
if (playerCount >= 2) {
currentStatus = GameStatus.PLAYING;
System.out.println("게임이 시작되었습니다!");
// 반복문 블록 스코프
for (int i = 0; i < playerCount; i++) {
Player player = players.get(i);
player.initialize(); // i는 여기서만 유효
}
} else {
System.out.println("플레이어가 부족합니다.");
}
}
}
public String getStatusMessage() {
// Switch문에서 Enum 활용
switch (currentStatus) {
case READY:
return "게임 준비 완료";
case PLAYING:
return "게임 진행 중";
case FINISHED:
return "게임 종료";
default:
return "알 수 없는 상태";
}
}
}
Java의 변수 스코프, final 키워드, 상수 패턴을 공부하면서 느낀 것은 각각이 모두 연결되어 있다는 점입니다. 스코프를 이해하면 메모리를 효율적으로 사용할 수 있고, final을 활용하면 안전한 코드를 작성할 수 있으며, enum을 적절히 사용하면 유지보수하기 좋은 코드를 만들 수 있습니다.
특히 enum은 처음엔 복잡해 보일 수 있지만, 한 번 익숙해지면 정말 강력한 도구가 됩니다. 타입 안정성과 가독성, 두 마리 토끼를 모두 잡을 수 있거든요!
이런 개념들이 완벽히 이해되지 않더라도 괜찮습니다. 저도 처음엔 헷갈렸지만, 실제로 코드를 작성하면서 조금씩 익혀나갔거든요. 여러분도 하나씩 적용해보시면서 자연스럽게 체득하시길 바랍니다.
코딩 공부는 마라톤과 같다고 생각해요. 꾸준히, 그리고 즐겁게 해나가시길 응원합니다!
Java 식별자, 키워드, 주석, 코드 스타일 기본 규칙 완벽 가이드
Java를 배우면서 꼭 알아야 할 기본 규칙들을 체계적으로 정리해보았습니다. 식별자 작성 규칙부터 키워드, 주석 작성법, 그리고 코딩 스타일까지 - 이 모든 것들이 좋은 Java 코드를 작성하는 기
byteandbit.tistory.com
'JAVA' 카테고리의 다른 글
| 자바 표준 입력·출력 완전 정복: Scanner와 System.out 활용 가이드 (0) | 2025.09.24 |
|---|---|
| Java 기본 자료형과 래퍼 타입: 박싱/언박싱으로 알아보는 핵심 개념과 실무 활용법 (0) | 2025.09.23 |
| Java 식별자, 키워드, 주석, 코드 스타일 기본 규칙 완벽 가이드 (0) | 2025.09.21 |
| 자바 프로젝트 구조 완벽 가이드: 소스에서 바이너리까지 빌드 흐름 쉽게 이해하기 (0) | 2025.09.20 |
| Java 컴파일과 실행 과정 완벽 가이드: JVM 작동 원리 이해하기 (0) | 2025.09.19 |