본문 바로가기

자바봄/리뷰

3. 상황에 따라 클래스변수/메서드와 인스턴스 변수/메서드를 적절하게 사용하자

 

 

1. 클래스변수와 인스턴스변수

변수의 종류는 세가지가 있습니다.

 

클래스변수, 인스턴스변수, 지역변수

 

각각이 의미하는 것은 아래 코드로 대체하겠습니다.

public class JavaBom {

    private static String name;  // 클래스 변수
    private static final String ALIAS = "Javabom"; // 클래스변수, 상수변수

    private String contents; // 인스턴스 변수

    public void hello(){
        String javabom = "Hello, Javabom!"; // 지역변수
        System.out.println(javabom);
    }

}

 

 

하나하나 살펴보면 

클래스변수는 JVM 클래스로더 초기화단계에 메모리에 적재됩니다. 그리고 이름에서 알 수 있다시피 클래스와 연결되어있습니다.

다시말해, 이 JavaBom 클래스를 통해 생성된 인스턴스는 모두 같은 name 변수를 공유합니다. 이 이야기는 멀티스레드와도 연결됩니다.

 

인스턴스 변수는 말 그대로 인스턴스의 변수입니다. new Javabom() 시 인스턴스마다 할당됩니다.

즉, 다른 인스턴스라면 다른 contents 변수를 가집니다. 서로 철저히 "격리"되어 있습니다.

 

마지막으로 지역변수는 메서드 안에서만 사용되는 변수를 말합니다.

@DisplayName("인스턴스 변수는 인스턴스별로 할당된다")
    @Test
    void instance() {
        JavaBom javaBom1 = new JavaBom();
        JavaBom javaBom2 = new JavaBom();

        javaBom1.setContents("javabomSet1");
        javaBom2.setContents("javabomSet2");

        assertThat(javaBom1.getContents()).isNotEqualTo(javaBom2.getContents());
    }

    @DisplayName("클래스변수는 모든 인스턴스가 공유한다.")
    @Test
    void classValue() {
        JavaBom javaBom1 = new JavaBom();
        JavaBom javaBom2 = new JavaBom();

        javaBom1.setName("javabom1");
        javaBom1.setName("javabom2");

        assertThat(javaBom1.getName()).isEqualTo(javaBom2.getName());
        assertThat(javaBom1.getName()).isEqualTo("javabom2");
    }

 

인스턴스 변수는 각 인스턴스의 "상태"를 표현할 때 적합합니다.

클래스변수는 모든 인스턴스가 공유할 필요가 있을 때 적합합니다.

 

그런데 위와 같이 모든 인스턴스가 접근 가능한 클래스 변수는 매우 위험합니다.

멀티스레드 환경에서 해당 변수에 접근한 스레드가 값을 변경한다면 다른 스레드는 그 내용을 알지 못하기 때문에 큰 문제가 발생할 수 있습니다. 

 

따라서, 클래스 변수를 사용하는 경우는 대부분 final 을 함께 붙여 값을 재할당 할 수 없는 상수필드로 사용하는 경우로 제한합니다. 

 

아래 경우는 모든 인스턴스가 같은 ALIAS 레퍼런스를 보고있지만 값을 재할당 할 수 없기 때문에(get 만 할 수 있기 때문에) 스레드세이프 하다고 할 수 있습니다.

 

 

 

2. 클래스메서드와 인스턴스메서드

 

변수와 동일합니다 

 

 

인스턴스가 호출해야하는 printName 메서드가 인스턴스메서드

인스턴스가 없이도 클래스로 바로 호출 가능한 printMessage가 클래스메서드입니다.

 

인스턴스메서드를 사용하는 시점에는 인스턴스가 생성되어있기 때문에 인스턴스변수, 클래스변수 모두 사용가능합니다.

하지만 클래스메서드를 사용하는 시점에는 인스턴스 변수가 만들어져있지 않기 때문에 클래스변수만 사용가능합니다.

 

대신 클래스변수는 클래스명.메서드로 호출이 가능하기 때문에 인스턴스 생성 비용을 아낄 수 있습니다.

 

위와같이 인스턴스 생성 없이 사용할 수 있다.

 

 

따라서, 재사용이 잦아 비용이 높고, 인스턴스 변수를 사용하지 않은 경우에는 

상수(static final)필드와 클래스메서드의 조합으로 이루어진 Util성 클래스를 만들어 사용하기도 합니다