정의

클래스 레벨에서 변수를 선언하거나 메서드를 정의할 때 사용되는 키워드
클래스 자체에 속하는 고유한 멤버를 나타냄
즉, 특정 객체에 속하지 않고 클래스에 속하므로 모든 객체가 해당 멤버를 공유
- 메모리 효율성과 성능 면에서 이점이 있으며, 객체 생성 없이 활용할 수 있는 장점이 있음
- 공유 데이터나 유틸리티 메서드에 적합
- 그러나 무분별하게 사용 시, 캡슐화와 데이터 은닉을 위반할 수 있고 코드의 유연성을 해칠 수 있음

Static 변수 / 메서드 / 클래스

Static 변수 메서드 클래스
요약 클래스가 공유하는 변수 객체 없이 호출 가능한 클래스 메서드 외부 클래스 인스턴스 없이 사용할 수 있는 중첩 클래스
정의 클래스가 메모리에 로드될 때 한 번만 생성되며, 클래스에 속하는 모든 객체가 공유 객체가 아닌 클래스 자체에서 호출할 수 있는 메서드 일반적으로 클래스는 static으로 선언하지 않지만, 내부 클래스는 static으로 선언 가능
특징 개별 인스턴스마다 복사되는 인스턴스 변수와 달리, 모든 객체에서 하나의 static 변수를 공유 인스턴스 변수나 인스턴스 메서드를 사용할 수 없음
대신 static 변수나 다른 static 메서드만 호출할 수 있음
this 키워드 사용 불가
외부 클래스에 속하면서도 외부 클래스의 인스턴스에 독립적
외부 클래스의 인스턴스를 만들지 않고도 static 내부 클래스의 인스턴스를 생성할 수 있음
사용 모든 인스턴스에서 공유되어야 하는 데이터를 관리하거나, 특정 값이 오직 하나만 존재해야 할 때 사용 객체를 생성하지 않고도 호출할 수 있어야 하는 유틸리티 메서드(예: Math.pow() 등)에 유용 특정 클래스에만 종속된 클래스를 만들고, 해당 클래스의 인스턴스를 만들 필요 없이 사용하고자 할 때 유용  
예시 private final static int fixedRate; Collections.sort()
String.join()
OuterClass.NestedStaticClass nested = new OuterClass.NestedStaticClass();

예시 코드
// Static 변수 (클래스 변수)

public class Counter {
    public static int count = 0;

    public Counter() {
        count++;
    }
}

// 사용 예시
Counter a = new Counter();
Counter b = new Counter();
System.out.println(Counter.count);  // 출력: 2, 두 객체가 같은 count 변수를 공유​
// Static 메서드 (클래스 메서드)

public class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

// 사용 예시
int result = MathUtils.add(5, 10);  // 객체 생성 없이 클래스명으로 호출 가능
// Static 클래스 (내부 클래스에서 사용)

public class OuterClass {
    static class NestedStaticClass {
        public void display() {
            System.out.println("Static nested class method.");
        }
    }
}

// 사용 예시
OuterClass.NestedStaticClass nested = new OuterClass.NestedStaticClass();
nested.display();  // "Static nested class method." 출력



Static과 Non-static을 요소의 성격에 따라 구분하여 적용하는 예시 코드

// Static과 Non-static 예시
public class BankAccount {
    // 모든 계좌에 공통된 금리를 위한 static 변수
    private static double interestRate = 0.05;

    // 개별 계좌마다 고유한 잔액을 저장하는 non-static 변수
    private double balance;

    // 생성자
    public BankAccount(double balance) {
        this.balance = balance;
    }

    // 모든 계좌에 공통 금리를 반환하는 static 메서드
    public static double getInterestRate() {
        return interestRate;
    }

    // 개별 계좌의 잔액을 반환하는 non-static 메서드
    public double getBalance() {
        return balance;
    }

    // 개별 계좌에 이자를 계산하여 추가하는 non-static 메서드
    public void applyInterest() {
        balance += balance * interestRate;  // 모든 계좌에 동일한 interestRate 적용
    }
}


public class Main {
    public static void main(String[] args) {
        // static 변수와 메서드는 클래스명으로 접근
        System.out.println("Interest Rate: " + BankAccount.getInterestRate());

        // 개별 계좌 생성
        BankAccount account1 = new BankAccount(1000);
        BankAccount account2 = new BankAccount(2000);

        // 개별 계좌마다 다른 잔액 확인
        System.out.println("Account 1 Balance: " + account1.getBalance());  // 1000
        System.out.println("Account 2 Balance: " + account2.getBalance());  // 2000

        // 이자 적용 후 잔액 확인
        account1.applyInterest();
        account2.applyInterest();
        System.out.println("Account 1 Balance after interest: " + account1.getBalance());
        System.out.println("Account 2 Balance after interest: " + account2.getBalance());
    }
}

 

여기서 만약에 private static double interestRate = 0.05; 선언에서 static을 뺀다면, interetRate는 각 개별 BankAccount 객체에 종속된 인스턴스 변수가 된다.
즉, 각각의 객체가 고유의 interestRate 값을 가질 수 있게 된다. (= 개별 객체가 서로 다른 이율을 사용할 수 있음)
또한 클래스명으로 접근할 수 없고 인스턴스를 통해서만 접근할 수 있으며, 각 객체마다 별도로 메모리에 할당된다. -> 효율 저하

한편, Static을 사용하지 않는 이유

1. 객체 지향성 저해
static 멤버는 인스턴스가 아닌 클래스에 귀속되어 객체지향 프로그래밍에서 중요한 캡슐화와 데이터 은닉 원칙을 위반할 수 있음

2. 테스트와 유연성 저하
static으로 선언된 변수와 메서드는 모킹(mocking)이 어렵고, 다른 클래스와 유연하게 협력하기 어려움

3. 메모리 관리
static 멤버는 프로그램 종료 시까지 메모리에 남아 있어 메모리 누수나 성능 저하를 유발할 수 있음

전역 변수와 static 변수

static 변수는 전역 변수와 비슷한 개념처럼 보이지만, 자바에서 전역 변수는 없음
자바는 전역 변수 대신 static 변수를 사용하여 비슷한 역할을 수행하게 함. 다만 전역 변수와는 몇 가지 중요한 차이점이 있음:

차이점 전역변수 static 변수
소속 어떤 클래스에도 속하지 않고 프로그램 전체에서 사용할 수 있음 자바에서 모든 변수는 반드시 클래스에 속해야 함
static 변수가 전역변수의 역할을 대신함
캡슐화 접근 제어가 어려움 private, protected, public 등 접근 제한자를 통해 캡슐화할 수 있음
불필요한 접근을 막고, 필요에 따라 클래스 내에서만 사용할 수 있게 하거나 제한된 외부 접근을 허용
객체지향적 설계 프로그램 내 어디서든 직접 접근할 수 있어 유지보수가 어렵고 불안정 클래스 안에 속해 있어 특정 클래스와 논리적으로 연결된 상태로 유지 = 객체 지향적 설계 원칙 따름
// 전역 변수처럼 사용되지만 캡슐화와 객체 지향 원칙 유지하는 예시

public class GlobalCounter {
    // 전역 변수처럼 사용 가능한 static 변수
    private static int count = 0;

    public static void increment() {
        count++;
    }

    public static int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) {
        // 클래스명으로 접근, 객체 생성 없이 사용
        GlobalCounter.increment();
        System.out.println(GlobalCounter.getCount());  // 출력: 1

        GlobalCounter.increment();
        System.out.println(GlobalCounter.getCount());  // 출력: 2
    }
}​

비고

1. 왜 private final static으로 선언하는지도 이제 명확한 근거를 들어 설명할 수 있다.

레퍼런스

1. 멘토링 강의, 도서관, 코드 예시의 경우 ChatGPT

 

'개발지식 조각 > Java' 카테고리의 다른 글

도메인과 서비스  (1) 2024.11.29
일급 컬렉션(First-Class Collection)  (1) 2024.10.31
클린 코드의 정의  (0) 2024.10.31
List, Set, Map, Queue  (0) 2024.10.31
제너럴한 명명규칙 정리  (0) 2024.10.31

+ Recent posts