본문 바로가기

Backend/Java

[Java] static 제어자 이해하기

이번 포스팅에서는 자바의 static 키워드에 대해 이해해보는 시간을 갖도록 하겠습니다.

 

static 키워드는 '클래스의' 혹은 '공통의'라는 의미를 내포하고 있습니다.

 

가령

class Cards {
    static int width = 5;
    static int height = 4;

    String kind;
    int numberOfCard;

    public Cards(String k, int n) {
        this.kind = k;
        this.numberOfCard = n;
    }
}

 

다음과 같은 class가 있다고 해보겠습니다.

 

저희는 Card Class를 통해 여러 가지의 Card 인스턴스를 만들 수 있습니다. 가령 클로버 4의 카드, 스페이드 A의 카드, 하트 3의 카드 등등 문양별로, 숫자별로 카드를 만들 수 있습니다.

 

이를 코드로 구현하면 다음과 같이 인스턴스를 생성할 수 있습니다. 

 

public class MainClass {
    public static void main(String[] args) {
        Cards first_card = new Cards("clover", 4);
        Cards second_card = new Cards("spade", 7);
        Cards third_card = new Cards("heart", 3);
        Cards fourth_card = new Cards("diamond", 9);
    }
}
class Cards {
    static int width = 5;
    static int height = 4;

    String kind;
    int numberOfCard;

    public Cards(String k, int n) {
        this.kind = k;
        this.numberOfCard = n;
    }
}

 

즉 저희는 Cards라는 어떠한 class를 각각 인스턴스의 특징에 맞게 생성해 준 것입니다.

 

여기서 width와 height는 변하면 안됩니다. 왜냐하면 각각 카드마다 높이와 너비가 다르면 머리 좋은 플레이어가 뒤집어져있는 카드의 크기로 예측할 수 있겠죠? 따라서 카드가 다른 문양과 숫자를 갖더라도 높이와 너비는 절대 바뀌면 안 되는 수치가 되는 것입니다.

 

여기서 static 키워드가 사용되어야 하는 이유가 나옵니다.

 

1. static 변수는 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 됩니다.

 

2. 인스턴스가 굳이 생성되지 않더라도 사용 가능합니다.

 

3. static 변수는 클래스가 메모리에 로드될때 생성되기 때문에 상대적으로 생성에 필요한 시간을 줄일 수 있고, 접근에 필요한 시간을 줄일 수 있습니다.

 

 

즉 다음과 같은 결론을 도출할 수 있습니다.

 

1. 클래스를 설계할 때 멤버변수 중 모든 인스턴스가 공통적으로 가져야 하는 속성이라면 static을 붙입니다. 

그렇게 되면 저희는 공통적인 속성을 모든 인스턴스가 공유할 수 있고, 빠르게 공통적인 속성을 생성해낼 수 있습니다.

 

비슷한 맥락으로 static method역시 이해 가능합니다. 

 

class Cards {
    static int width = 5;
    static int height = 4;

    String kind;
    int numberOfCard;
    
    public Cards(String k, int n) {
        this.kind = k;
        this.numberOfCard = n;
    }
    
    public void ShowWidthAndHeight() {
        System.out.println("width="+width+", height="+height);
    }
    public void ShowKindsAndNumOfCard() {
        System.out.println("kind="+kind+", numberOfCard="+numberOfCard);
    }
}

 

다음과 같이 두개의 메서드를 추가해보겠습니다. 하나는 static 변수인 width와 height을 출력하는 메서드이고 하나는 인스턴스 변수인 kind와 numberOfCard를 출력하는 메서드입니다.

 

public class MainClass {
    public static void main(String[] args) {
        Cards first_card = new Cards("clover", 4);
        Cards second_card = new Cards("spade", 7);
        Cards third_card = new Cards("heart", 3);
        Cards fourth_card = new Cards("diamond", 9);

        first_card.ShowWidthAndHeight();
        first_card.ShowKindsAndNumOfCard();

        second_card.ShowWidthAndHeight();
        second_card.ShowKindsAndNumOfCard();

        third_card.ShowWidthAndHeight();
        third_card.ShowKindsAndNumOfCard();

        fourth_card.ShowWidthAndHeight();
        fourth_card.ShowKindsAndNumOfCard();
    }
}

 

다음과 같이 출력해보겠습니다.

 

width=5, height=4
kind=clover, numberOfCard=4
width=5, height=4
kind=spade, numberOfCard=7
width=5, height=4
kind=heart, numberOfCard=3
width=5, height=4
kind=diamond, numberOfCard=9

 

출력 결과는 위와 같이 나옵니다. 여기서 static 키워드를 붙여보겠습니다. 

 

class Cards {
    static int width = 5;
    static int height = 4;

    String kind;
    int numberOfCard;

    public Cards(String k, int n) {
        this.kind = k;
        this.numberOfCard = n;
    }

    public static void ShowWidthAndHeight() {
        System.out.println("width="+width+", height="+height);
    }
    
    // error
    public static void ShowKindsAndNumOfCard() {
        System.out.println("kind="+kind+", numberOfCard="+numberOfCard);
    }
}

 

밑의 메서드는 에러가 발생합니다. 그 이유는 static 메서드에 인스턴스 변수를 사용하였기 때문입니다.

 

위의 멤버변수의 static 선언과 비슷한 맥락으로 메서드에 static 키워드를 붙여주게 되면 클래스가 메모리에 로드될 때 하나의 멤버처럼 같이 로드됩니다. 당연히 추후에 동적으로 생성되는 인스턴스에 대한 정보가 없는 상태이기 때문에 static 변수가 아닌 인스턴스 변수가 사용되면 error가 발생합니다.

 

public static void ShowWidthAndHeight() {
    System.out.println("width="+width+", height="+height);
}
public static void ShowKindsAndNumOfCard(String kind, int numberOfCard) {
    System.out.println("kind="+kind+", numberOfCard="+numberOfCard);
}

 

하지만 이렇게 매개변수로 전달되면 static 키워드를 선언할 수 있습니다. 이렇게 되면 인스턴스 변수를 사용하지 않기 때문에 문제가 발생하지 않게 됩니다.

 

하지만 보시다싶이 위의 메서드처럼 선언하면 코드가 비효율적이게 되는 것이 눈에 보입니다. kind와 numberOfCard는 모두 인스턴스 변수이기 때문에 그냥 static을 선언하지 않는 것이 코드의 간결성을 더 높여줍니다.

 

결론을 짓자면,

 

1. static 메서드에서는 인스턴스 변수를 사용할 수 없습니다. (반대로 인스턴스 메서드에서는 static 변수를 사용 가능한데, 인스턴스 생성시점에는 이미 클래스 변수가 생성되어 메모리에 올라가 있기 때문입니다)

 

따라서

 

2. 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을 붙일 것을 고려해봅니다. 

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.