배움 기록_실무 ✏️

static의 모든 것

j_estory 2022. 10. 10. 18:36
  • static 변수
public calss MyClass {
	static int myStaticVariable = 100;
    int myVariable = 200;
}
  • 위의 예제로 각 변수가 생성되는 공간에 대해 설명해본다.

  • 자바 프로그램을 시작하면 JVM이 클래스 파일을 읽어 필요한 클래스와 변수/메서드들을 메모리에 생성한다.
  • 이후 코드에서 그 클래스의 형을 가진 오브젝트를 생성하면 각 오브젝트마다 myVariable을 저장할 메모리 공간이 Heap이라는 메모리 공간에 생기게 된다. 모든 오브젝트는 Heap이라고 부르는 메모리 공간에 생긴다.
  • 반면 static 변수는 Heap이 아닌 다른 공간에 생긴다. 이 공간의 이름은 MetaSpace이다.
  • 위의 그림처럼 static 변수는 클래스에 귀속되지 각각의 오브젝트에는 귀속되지 않는다.
    • 평범한 변수는 object마다 1개 생성되므로 n개의 object가 있을 때 n개 생성된다. 
    • sstatic 변수는 object가 몇 개이던 단 1개만 생성된다.
  • EX 1. 실습
public class Main {
	public static void main(String[] args) {
    	System.out.println(MyClass.myStaticVariable);
    }
}
  • MyClass에 존재하는 myStaticVariable에 접근하려면 ?
    • static 변수는 클래스에 귀속되므로 이 변수를 사용하기 위해서는 <클래스이름>.<static변수이름> 으로 접근해야 한다.
    • 클래스에 속하므로 object를 생성하지 않아도 myStaticVariable을 사용할 수 있다.
public class Main {
	public static void main(String[] args) {
    	System.out.println(new MyClass().myVariable);
    }
}
  • 멤버 변수인 myVarialbe은 'new MyClass()'와 같이 새 오브젝트를 생성하고 이 오브젝트를 참조 해야만 해당 변수를 사용할 수 있다.
public class Main {
	public static void main(String[] args) {
    	new MyClass().myMethod();
        System.out.println("----------------");
        new MyClass().myMethod();
    }
}

// 실행 결과
/**
    101
    201
    --------------
    102
    201
**/
  • 위와 같은 실행 결과가 나온 이유 ?
    • 자바를 실행시키면 JVM이 static 변수를 먼저 MetaSpace에 만든다.
    • 이후 메인 메서드가 실행되면서 새 object들이 heap 공간에 생성된다.
    • myVariable은 object 당 하나 생성되므로 각각의 값이 1씩 늘어나지만 myStaticVariable은 클래스 당 1개 존재하므로 '공유' 변수처럼 사용된다.
  • static 메서드
public class MyClass {
	static int myStaticVariable = 100;
    int myVariable = 200;
    
    public void myMethod() {}
    
    public static void myStaticMethod() {}
}

  • static 메서드도 마찬가지로 MetaSpace에 만들어진다.
  • 한가지 실습을 해보자! 각각의 메소드에서 myStaticVariable과 myVariable을 사용해보는 것이다.
public class MyClass {
	static int myStaticVariable = 100;
    int myVariable = 200;
    
    public void myMethod() {
    	myStaticVariable++;
        myVariable++;
        System.out.println(myStaticVariable);
        System.out.println(myVariable);
    }
    
    public static void myStaticMethod() {
        myStaticVariable = 3;
        myVariable = 4; // non-static field 'myVariable' cannot be referenced from static context
    }
}
  • 위의 코드에서 myVariable = 4에서 신택스 에러가 난다. 이유는 뭘까?
    • MyVlass 안에 myVariable은 설계도이다. 이 뜻은 '언젠가 이 클래스를 이용해 오브젝트를 생성하게 되면 myVariable이라는 변수를 만들고 200으로 초기화를 해라' 라는 뜻이다.
    • 하지만 myStaticMethod는 자바 프로그램 실행 당시 자바가 클래스 인식을 하면서 static 변수/메서드는 MetaSpace에 먼저 넣어놓게 되어 이때는 myVariable은 메모리 상에 존재하지 않으므로 사용할 수 없다.
    • 따라서 myStaticMethod는 비록 같은 클래스 안에 정의되어 있지만 object 생성 시 생성되는 일반적인 멤버 변수/메서드로 볼 수 없다. 
  • ⁉️ 여기서 궁금증이 생긴거는 그러면 static method는 어느때 사용하면 좋을까 의문이 든다.

 

  • main 메서드
  • 이제 왜 main 앞에 static이 붙는지 알겠다.
    • 클래스를 로딩하면서 main 메서드가 메모리에 저장되고 자바는 메인 메서드를 보고 제일 먼저 call해야 겠다고 이해 하는 것이다.
더보기

참고 !
클래스는 반드시 object를 생성해 사용한다고 했는데 우리는 main 메서드가 있는 Main 클래스의 object를 만든적이 없다.

다만 public static void main(Stirng[] args)라는 형식을 가진 메서드를 자바가 가장 먼저 실행시킨다는 특징이 있다.

 

  • static class
    • static class에 대하여 설명하기 전에 내부 class의 정의 부터 살펴보자!
public class Test {

    class InnerClass {
        // InnerClass
    }

    static class InnerStaticClass {
        // static InnerClass
    }

    public static void main(String[] args) {
        Test.InnerClass innerClass1 = new Test().new InnerClass();
        Test.InnerClass innerClass2 = new Test().new InnerClass();

        if (innerClass1 == innerClass2) {
            System.out.println("내부 클래스는 같은 참조");
        } else {
            System.out.println("내부 클래스는 다른 참조");
        }
    }
}
  • 내부 class
    • 클래스 안에 클래스가 존재하는 형태이다. 
    • 보통 내부 클래스의 객체를 만들기 위해서는 상위 클래스의 객체를 먼저 만들어야 한다.
    • new 연산자를 두번 사용하여 외부 클래스에 대한 인스턴스를 사용하여 내부 클래스의 인스턴스를 생성한다.
    • 위의 코드를 실행 시켜 보면 두 인스턴스는 다른 참조를 갖고 있다.
public class Test {

    class InnerClass {
        // InnerClass
    }

    static class InnerStaticClass {
        // static InnerClass
    }

    public static void main(String[] args) {
        Test.InnerStaticClass innerClass1 = new Test.InnerStaticClass();
        Test.InnerStaticClass innerClass2 = new Test.InnerStaticClass();

        if (innerClass1 == innerClass2) {
            System.out.println("내부 클래스는 같은 참조");
        } else {
            System.out.println("내부 클래스는 다른 참조");
        }
    }
}
  • 내부 static class
    • 클래스의 객체 2개를 만들어 본 후, 두개의 인스턴스는 같을까?
      • 나의 생각은 결과가 같을거라고 생각했다. static 키워드가 붙었으니 변수, 메서드 처럼 이를 공유한다고 생각했다. 
      • 하지만 결과는 두개 인스턴스의 참조 값이 달랐다. 
      • 클래스의 역할은 인스턴스를 만드는 설계도의 역할을 할 뿐이지 그 자체가 인스턴스처럼 존재할 수는 없다. 
      • static 키워드가 클래스에 붙게 되면 인스턴스를 생성하는 방식이 달라지는 것이지 클래스가 갑자기 인스턴스의 역할을 하지는 못한다는 것이다.
    • 다만 static이 붙은 class는 상위 클래스의 객체를 만들지 않고 inner class를 사용할 수 있다는 점이 차이이다. 
  • 내부 클래스를 사용하는 이유
    • 외부 클래스와 내부 클래스가 긴밀한 관계를 맺고 있을 때 사용한다.
    • 내부 클래스에서 외부 클래스의 멤버에 쉽게 접근 할 수 있다.
    • 서로 관련있는 코드를 묶어서 캡슐화를 증가시킬 수 있다. 
    • 외부에서는 내부 클래스에 접근할 수 없기 때문에 코드의 복잡성도 줄일 수 있다. 
  • 내부 클래스와 static 내부 클래스의 차이
public class Test {
    
    void myTest() {
        
    }

    class InnerClass {
        void innerClassMethod() {
            Test.this.myTest();
        }
    }

    static class InnerStaticClass {
        Test.this.myTest();    // 에러
    }
}
  • 위의 코드에서 static 내부 클래스에서 Test 클래스의 메소드에 접근하게 되면 컴파일 에러가 발생한다. 
    • 이유 : Test 객체를 만들기 전에 InnerStaticClass를 사용할 수 있기 때문에 참조할 수 없다는 것이다. 
  • 이러한 단점에도 static을 사용하지 않는 것의 단점이 더 크기 때문에 내부 클래스는 가능한 static으로 만들어야 한다.
    • static이 붙지 않았을 때의 단점
      • 참조값을 담아야 하기 때문에 인스턴스 생성시 시간적, 공간적 성능이 낮아진다.
      • 외부 인스턴스에 대한 참조가 존재하기 때문에, 가비지 컬렉션이 인스턴스 수거를 하지 못하여 메모리 누수가 생길 수 있다. 
    • 결론 : static 키워드를 사용하고 싶지 않다면, 내부 클래스가 아닌 별겨의 클래스로 만드는 것이 좋다.

✅ 정리

  • static 변수/메서드는 클래스당 하나 생성된다.
  • 프로젝트가 하나의 변수를 공유해야 할 경우 static 변수를 사용한다.
  • static 변수/메서드를 사용하기 위해서는 object를 사용할 필요가 없다. 

📘 언제 사용할까?

  • 프로젝트가 하나의 변수를 공유해야 할 경우 static 변수를 사용한다.
  • object의 멤버 변수에 구애 받지 않은 경우, object 생성 없이 메서드를 사용하고 싶은 경우 static 메서들 사용한다.

📌 static Class

  • static은 클래스에서도 사용할 수 있지만 이는 중첩 클래스(nested class)를 알아야 하므로 다음에 바로 다루도록 하겠다!

 

END

  • 실무를 하면서 자바의 기초가 얼마나 중요한지 알게 되어 공부를 시작하게 되었다.
  • static 키워드를 공부하며 의문점이 생긴 부분이 몇가지 있어 이어서 공부를 해볼까 한다. 
  • 또한 JVM의 동작방식도 궁금하여 해당 내용도 공부해봐야겠다 싶다! 

'배움 기록_실무 ✏️' 카테고리의 다른 글

AWS Simple Queue Service (SQS)  (0) 2023.02.23
정적 메서드(static method)  (0) 2022.10.17
AWS Database Migration Workshop  (1) 2022.05.25
구글 OAuth2 인증 방식  (0) 2022.03.29
OAuth 동작 방식  (1) 2022.03.29