2018-2-28 TIL 클래스변수와 클래스메소드

클래스변수와 클래스메소드

  • 인스턴스변수는 인스턴스가 생성될 때, 생성된 인스턴스 안에 존재하는 변수이다. 그러나 ‘클래스변수’는 인스턴스의 생성과 상관없이 존재하는 변수이다.
package yoon;

class InstCnt{
    static int instNum = 0; // class varable

    InstCnt(){ // Constructor
        instNum++;
        System.out.println("인스턴스 생성 : " + instNum);
    }
}

public class ClassVar {
    public static void main(String [] args){
        InstCnt ctn1 = new InstCnt();
        InstCnt ctn2 = new InstCnt();
        InstCnt ctn3 = new InstCnt();
    }
}

//result
인스턴스 생성 : 1
인스턴스 생성 : 2
인스턴스 생성 : 3
  • 클래스변수는 인스턴스 내에 존재하는 변수가 아니라 ‘어떠한 인스턴스에도 속하지 않는 상태로 메모리 공간에 딱 하나만 존재하는 변수’ 이다.

클래스 변수의 접근 방법

  1. 클래스 내부 접근 : 변수의 이름을 통해 직접 접근
  2. 클래스 외부 접근 : 클래스 또는 인스턴스의 이름을 통해 접근
package yoon;

class AccessWay{
    static int num = 0;

    AccessWay(){
        incrCnt();
    }

    void incrCnt() {
        num++; // 클래스 내부에서 이름을 통한 접근
    }
}

public class ClassVarAccess {
    public static void main(String [] args){
        AccessWay way = new AccessWay();
        way.num++; // 외부에서 인스턴스의 이름을 통한 접근
        AccessWay.num++; // 외부에서 클래스 이름을 통한 접근
        System.out.println("num = " + AccessWay.num);
    }
}
//result
num = 3
  • 클래스 변수 num은 default로 선언이 되어있다. 따라서 클래스 내부는 물론 클래스 외부이더라도 동일 패키지로 묶여 있으면 접근이 가능하다!!

클래스 변수의 초기화 시점과 초기화 방법

package yoon;

class InstCnt2 {
    static int instNum = 100;

    InstCnt2(){
        instNum++;
        System.out.println("인스턴스 생성 : " + instNum);
    }
}

public class OnlyClassNoInstance {
    public static void main(String [] args){
        InstCnt2.instNum -= 15; // 인스턴스 생성없이 instNum에 접근
        System.out.println(InstCnt2.instNum);
    }
}
//result
85
  • 위의 코드를 통해 클래스 변수는 인스턴스 생성 이전에 메모리 공간에 존재하는것을 알 수 있다.
  • 클래스 변수는 해당 클래스 정보가 가상머신에 의해 읽히는 순간 메모리 공간에 할당되고 초기화 된다.

클래스 메소드

  • 클래스 내에 정의된 메소드에 static을 선언하면, ‘클래스메소드’가 된다. 접근방법도 클래스변수와 동일하며, 인스턴스 생성이전부터 호출이 가능하고 어느 인스턴스에도 속해있지 않다.
package yoon;

class NumberPrinter {
    private int myNum = 0;

    static void showInt(int n){ // class method
        System.out.println(n);
    }

    static void showDouble(double n){
        System.out.println(n);
    }

    void setMyNum(int n){
        myNum = n;
    }

    void showMyNumber(){ // instance method
        showInt(myNum);
    }
}

public class ClassMethod {
    public static void main(String [] args){
        NumberPrinter.showInt(20);
        // 클래스 이름을 통한 클래스 메소드 호출

        NumberPrinter np = new NumberPrinter();
        np.showDouble(3.15); // 인스턴스 이름을 통한 클래스 메소드 호출
        np.setMyNum(75);
        np.showMyNumber();
    }
}
//result
20
3.15
75

Q. 클래스 메소드에서 같은 클래스에 선언된 인스턴스 변수에 접근이 가능한가?

class AAA {
	int num = 0;
    static void addNum(int n) {
    	num += n; // 이 문장이 유효한가?
    }
}
  • A. 클래스 메소드는 인스턴스에 속하지 않으므로 인스턴스 변수에 접근이 불가능하다.
  • A. 같은 이유로 클래스 메소드는 인스턴스 메소드의 호출도 불가능하다.

System.out.println() 그리고 public static void main()

  • System은 java.lang패키지에 묶여있는 기본클래스이다. 원칙적으로는 Java.lang.System.out.println(…);으로 호출을 해야한다. 그러나 컴파일러가 다음 문장을 삽입해주기 때문에 패키지의 이름부분을 생략할 수 있다.
import Java.lang *;
// 컴파일러가 삽입하는 import선언
  • out은 Systme.out으로 접근을 하니, static으로 선언된 클래스 변수라고 추측이 가능하다.
public final class System extends Object {
	public static final PrintStream out;
    // 참조변수 out
}
  • println은 PrintStream의 인스턴스 메소드이다.
    Systme.out.println(...);
    
  • System에 위치한 클래스 변수 out이 참조하는 인스턴스의 println 메소드를 호출하는 문장

main메소드가 public이고 staitc인 이유는?

  • main메소드 다음과 같은 구성이다
public static void main(String [] args){
	...
}
  • main메소드의 호출이 이뤄지는 영역은 클래스 외부이다. 따라서 public으로 선언이 되어한다.
  • main메소드는 인스턴스가 생성되기 전에 호출된다. 따라서 static선언이 당연하다.

static의 또 다른 용도

static 초기화 블록(Static Initialization Block)

  • static 초기화 블록은 가상머신이 클래스의 정보를 읽어 들일 때, 실행이 된다.
package yoon;

import java.time.LocalDate;

public class DateOfExecution {
    static String date;

    static { // 클래스 로딩 시 단 한번 실행이 되는 영역
        LocalDate nDate = LocalDate.now();
        date = nDate.toString();
    }

    public static void main(String [] args){
        System.out.println(date);
    }
}
//result
2018-02-26
Written on February 28, 2018