0Pricing
Java Academy · 강의

정적 필드와 상수

공유 상태에는 정적 필드를, 상수에는 static final을, 정적 초기화 블록에는 정적 초기화 구문을 사용합니다.

정적 필드와 상수은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

정적 필드와 상수

정적 필드는 인스턴스가 아니라 클래스에 속합니다. 따라서 해당 클래스의 모든 객체가 공유합니다. 상수는 정적과 최종 특성을 결합하여 클래스 수준의 변경 불가능한 값을 만듭니다.

정적 필드와 인스턴스 필드 비교

인스턴스 필드는 객체마다 별도의 사본을 가집니다. 정적 필드는 해당 클래스의 모든 객체가 공유하는 하나의 사본만 가집니다.

class Counter {
    private static int totalCount = 0;  // shared across all instances
    private int instanceId;              // unique per instance

    Counter() {
        totalCount++;
        instanceId = totalCount;
    }

    static int getTotalCount() { return totalCount; }
    int getInstanceId()        { return instanceId; }
}

Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();

System.out.println(Counter.getTotalCount()); // 3
System.out.println(c1.getInstanceId());       // 1
System.out.println(c3.getInstanceId());       // 3

static final을 사용한 상수

상수는 public static final로 선언합니다. 관례적으로 대문자 스네이크 케이스를 사용합니다.

class MathConstants {
    public static final double PI      = 3.141592653589793;
    public static final double E       = 2.718281828459045;
    public static final double GOLDEN  = 1.618033988749895;
    public static final int    PRIMES[] = {2, 3, 5, 7, 11, 13};
}

// Access without an instance
System.out.println(MathConstants.PI);

// Constants on domain classes
class HttpStatus {
    public static final int OK        = 200;
    public static final int NOT_FOUND = 404;
    public static final int SERVER_ERR = 500;
}

정적 초기화 블록

상수를 복잡하게 초기화해야 할 때는 클래스를 로드할 때 한 번 실행되는 정적 초기화 블록을 사용합니다.

import java.util.*;

class Config {
    public static final Map<String, String> DEFAULTS;

    static {
        Map<String, String> map = new HashMap<>();
        map.put("timeout",  "30");
        map.put("maxRetry", "3");
        map.put("logLevel", "INFO");
        DEFAULTS = Collections.unmodifiableMap(map);
    }
}

System.out.println(Config.DEFAULTS.get("timeout")); // 30

정적 카운터 패턴

정적 필드는 ID 생성, 메트릭 집계 또는 공유 캐시에 흔히 사용됩니다.

import java.util.concurrent.atomic.AtomicLong;

class Order {
    private static final AtomicLong ID_SEQUENCE = new AtomicLong(1000);

    private final long id;
    private final String item;

    Order(String item) {
        this.id   = ID_SEQUENCE.getAndIncrement();
        this.item = item;
    }

    long getId()   { return id; }
    String getItem() { return item; }
}

Order o1 = new Order("Laptop");
Order o2 = new Order("Mouse");
System.out.println(o1.getId()); // 1000
System.out.println(o2.getId()); // 1001

정적 필드를 사용한 싱글턴

싱글턴에서는 정적 필드가 유일한 인스턴스를 보관합니다. 항상 필요하지 않을 수 있는 클래스에는 지연 초기화를 사용하세요.

class DatabasePool {
    private static volatile DatabasePool instance;
    private final int maxConnections;

    private DatabasePool() { this.maxConnections = 20; }

    public static DatabasePool getInstance() {
        if (instance == null) {
            synchronized (DatabasePool.class) {
                if (instance == null)
                    instance = new DatabasePool();
            }
        }
        return instance;
    }

    public int getMaxConnections() { return maxConnections; }
}

System.out.println(DatabasePool.getInstance().getMaxConnections()); // 20

정적 임포트

import static을 사용하면 클래스 이름 접두사 없이 정적 멤버를 사용할 수 있어 상수와 유틸리티 메서드에 유용합니다.

import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.Math.pow;

// Without import static: Math.PI * Math.pow(r, 2)
double radius = 5.0;
double area = PI * pow(radius, 2);
System.out.printf("Area: %.2f%n", area); // 78.54

// Caution: overuse of static imports hurts readability
// Acceptable for: Math.*, Assert.* in tests, TimeUnit

정적 필드의 스레드 안전성

여러 스레드가 공유하는 정적 필드에는 경쟁 조건을 피하기 위해 동기화나 원자적 형식이 필요합니다.

import java.util.concurrent.atomic.*;

class RequestMetrics {
    private static final AtomicLong requestCount  = new AtomicLong(0);
    private static final AtomicLong totalLatencyMs = new AtomicLong(0);

    public static void record(long latencyMs) {
        requestCount.incrementAndGet();
        totalLatencyMs.addAndGet(latencyMs);
    }

    public static double averageLatency() {
        long count = requestCount.get();
        return count == 0 ? 0 : (double) totalLatencyMs.get() / count;
    }
}
RequestMetrics.record(45);
RequestMetrics.record(120);
System.out.println(RequestMetrics.averageLatency()); // 82.5

변경 가능한 정적 필드: 주의점

변경 가능한 정적 필드는 위험합니다. 전역 변경 가능 상태는 테스트하기 어렵고, 주의하지 않으면 스레드 안전하지 않으며, 클래스 간에 숨은 결합을 만듭니다.

// BAD: mutable static state
class GlobalConfig {
    public static String environment = "dev"; // mutable global!
}
// Any code anywhere can change GlobalConfig.environment
// Very hard to test, unexpected interactions

// BETTER: immutable constant
class GlobalConfig2 {
    public static final String ENVIRONMENT =
        System.getenv().getOrDefault("APP_ENV", "dev");
}

// BEST: inject via constructor (dependency injection)
class Service {
    private final String environment;
    Service(String environment) { this.environment = environment; }
}

상수 인터페이스 안티패턴

상수를 보관하기 위해 인터페이스를 사용하는 방식인 상수 인터페이스 안티패턴은 권장되지 않습니다. 대신 비공개 생성자를 가진 클래스를 사용하세요.

// BAD: constant interface — pollutes the namespace of implementing classes
interface FinancialConstants {
    double TAX_RATE = 0.08;  // implicitly public static final
}

// GOOD: utility class for constants
final class FinancialConstants2 {
    private FinancialConstants2() {} // prevent instantiation

    public static final double TAX_RATE  = 0.08;
    public static final double VAT_RATE  = 0.20;
    public static final int    MAX_ITEMS = 100;
}
System.out.println(FinancialConstants2.TAX_RATE); // 0.08

실습: 상수를 통한 기능 플래그

컴파일 시점 기능 플래그에 정적 최종 불리언 상수를 사용하면 JIT 컴파일러가 실행되지 않는 분기를 제거합니다.

class Features {
    public static final boolean ENABLE_ANALYTICS   = true;
    public static final boolean ENABLE_AI_SEARCH    = false;
    public static final boolean DEBUG_MODE          =
        "true".equals(System.getenv("DEBUG"));
}

// The JIT eliminates this if-block when ENABLE_AI_SEARCH is false:
if (Features.ENABLE_AI_SEARCH) {
    System.out.println("Running AI search...");
}

if (Features.DEBUG_MODE) {
    System.out.println("[DEBUG] Request received");
}

빠른 확인

정적 필드와 인스턴스 필드의 차이는 무엇입니까?

복습: 정적 필드와 상수

핵심 정리:

  • 정적 필드는 클래스에 속하며 모든 인스턴스가 하나의 사본을 공유합니다
  • 상수는 공개 정적 최종 필드로 선언하고 대문자 스네이크 케이스를 사용합니다
  • 복잡한 초기화가 필요한 상수에는 정적 초기화 블록을 사용합니다
  • 동시성 코드에서 변경 가능한 정적 필드에는 AtomicLong/AtomicInteger를 우선 사용합니다
  • 변경 가능한 정적 필드는 피하세요 — 테스트하기 어려운 숨은 전역 상태를 만듭니다
  • 상수에는 인터페이스 대신 인스턴스화할 수 없는 클래스(비공개 생성자)를 사용합니다

자주 묻는 질문

“정적 필드와 상수” 강의는 무료인가요?

네 — “정적 필드와 상수” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“정적 필드와 상수”에서 뭘 배우나요?

공유 상태에는 정적 필드를, 상수에는 static final을, 정적 초기화 블록에는 정적 초기화 구문을 사용합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“정적 필드와 상수” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 메서드 오버로딩 규칙
  2. 가변 인자 매개변수
  3. 정적 필드와 상수
  4. 유틸리티 클래스와 정적 팩터리 메서드
← Java Academy(으)로 돌아가기