Java

[Java] Static 변수, static 메소드

딸기케잌🍓 2021. 5. 23. 13:43

 

static(정적)은 '고정된'이라는 의미를 가지고 있습니다.

변수나 메소드 앞에 static 키워드를 붙여서 static 변수나 static 메소드를 만들 수 있습니다.

이 둘을 정적 멤버 또는 클래스 멤버라고도 합니다.

정적 멤버는 객체에 속한 것이 아니고 클래스에 속합니다. 따라서 객체 간에 정적 멤버를 공유할 수 있습니다.

메모리 사용을 최대한 늦추기 위해 클래스가 처음으로 사용될 때 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재합니다.

처음으로 사용될 때라는 것은 다음 세 가지 중 하나입니다.

  • 클래스의 정적 속성(정적 변수, static{} 블록)을 사용할 때
  • 클래스의 정적 메서드를 사용할 때
  • 클래스의 인스턴스를 최초로 만들 때

이 때, 클래스별로 정적 멤버가 관리됩니다. 따라서 클래스의 로딩이 끝나는 즉시 사용할 수 있습니다.

 

정적 멤버는 어디에 생성될까

Static 멤버들은 Static 영역에 생성됩니다.

Static 영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든 참조할 수 있습니다.

new 연산을 통해 생성된 객체는 Heap 메모리 영역에 생성됩니다. Heap 영역의 메모리들은 Garbage Collector의 메모리 회수 대상입니다.

하지만 Static영역의 Static 멤버들은 GC의 관리 대상이 아니므로 프로그램의 종료시까지 메모리가 할당된 채로 존재합니다.

 

=>Static은 꼭 필요한 때에만 써야 합니다.

 

정적 멤버는 언제 사용할까

Static에 대한 판단 기준은 공용으로 쓰일지 아닐지에 따라 판단하면 됩니다. 

static 변수와 instance 변수

변수 앞에 static 키워드가 붙어있으면 static 변수이고 붙어 있지 않으면 instance 변수입니다.

빠른 이해를 위해 간단한 예제를 보겠습니다 ^>^

public class HouseName  {
    static String lastname = "Choi";
    String firstname = "";

    public static void main(String[] args) {
        HouseName person1 = new HouseName();
        HouseName person2 = new HouseName();
        
        person1.firstname="연두"
        person2.firstname="보라"
    }
}

Choi 성은 고정되었다고 가정했을 때 person1, person2 객체를 생성할 때마다 Choi성을 저장할 메모리를 따로 할당할 필요는 없습니다.

따라서 lastname 변수를 Static으로 선언하면 딱 한번만 메모리 할당을 하게되고 HouseName 클래스로 생성된 객체들은 같은 곳의 메모리 주소만을 바라보기 때문에 Static 변수인 lastName을 공유할 수 있습니다.

lastname인 Choi 성이 바뀌지 않는경우 final 키워드를 이용하여 고정된 값이라고 명시해 줄 수 있습니다.

 

그러나 firstname인 이름은 사람마다 다르므로 일반 인스턴스 변수로 선언하여 각 인스턴스마다 다른 값을 유지하도록 했습니다.

 

 

static 메소드와 instance 메소드

변수와 마찬가지로 메소드 앞에 static 키워드가 붙어있으면 static 메소드(클래스 메소드)이고 붙어 있지 않으면 instance 메소드입니다.

어느경우에 static을 사용하여 클래스 메소드로 정의해야 할까요?

 

 

클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'이라고 할 수 있습니다같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있습니다인스턴스메서드는 인스턴스변수와 관련된 작업을 하는즉 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드입니다.
그래서 메서드 내에서 인스턴스 변수를 사용하지 않거나 클래스 변수만을 사용하는 메서드들은 클래스 메서드로 일반적으로 정의합니다.

 

class MyMath2 {
      long a, b;

      // 인스턴스변수 a, b를 이용한 작업을 하므로 매개변수가 필요없다.
      long add() {       return a + b; }
      long subtract() {       return a - b; }
      long multiply() {       return a * b; }
      double divide() {       return a / b; }

 
      // 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
      static long add(long a, long b) {       return a + b; }
      static long subtract(long a, long b) {       return a - b; }
      static long multiply(long a, long b) {       return a * b; }
      static double divide(double a, double b) {       return a / b; }
}

 

class MyMathTest2 {

      public static void main(String args[]) {

            // 클래스메서드 호출
            System.out.println(MyMath2.add(200L, 100L));
            System.out.println(MyMath2.subtract(200L, 100L));
            System.out.println(MyMath2.multiply(200L, 100L));
            System.out.println(MyMath2.divide(200.0, 100.0));


            MyMath2 mm = new MyMath2();
            mm.a = 200L;
            mm.b = 100L;

            // 인스턴스메서드는 객체생성 후에만 호출이 가능함.
            System.out.println(mm.add());
            System.out.println(mm.subtract());
            System.out.println(mm.multiply());
            System.out.println(mm.divide());

}

실행결과

[실행결과]
300
100
20000
2.0
300
100
20000
2.0

add(long a, long b), subtract(long a, long b) 등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스메서드로 선언하였습니다.

MyMathTest2 main메서드에서 보면클래스메서드는 객체생성없이 바로 호출이 가능했고인스턴스메서드는 MyMath2클래스의 인스턴스를 생성한 후에야 호출이 가능합니다.

=>정적 메소드는 공통 유틸리티 함수를 만드는데 유용하게 사용됩니다.

 

 

 

 

 

클래스 멤버와 인스턴스 멤버간의 참조와 호출

static 메소드는 기본적으로 static 변수에만 접근할 수 있습니다.

static 메소드는 객체의 생성 없이 사용할 수 있으므로 static 메소드에서 아직 생성되지 않은 변수에 접근할 위험이 있습니다. 따라서 static메소드에서 접근하기 위한 변수는 static 변수로 선언되어야 합니다. 

클래스멤버가 인스턴스멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성한 후에야 가능합니다.

반대로 인스턴스 변수나 인스턴스 메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능합니다. 인스턴스 변수가 존재한다는 것은 static 변수가 이미 메모리에 존재한다는 것을 내포하고 있기 때문입니다.

 

 

고민해야 할 Static 이슈

static 변수는 객체지향 프로그램의 원칙인 각 객체의 데이터들이 캡슐화되어야 한다는 원칙에 어긋나며 static 변수를 공유한 순간 서로에 영향을 주게 되어 어떤 사이드 이펙트가 발생할지 모른다고 생각됩니다.

또한 프로그램이 종료되기 전에 항상 메모리에 상주하고 있어 자주 사용하지 않는 메소드가 누적된다면 GC에 수거되지 못하므로 오히려 메모리 낭비가 발생합니다. 이러한 이유로 단순히 빠르다는 이유로 static 메서드 및 변수 사용을 지양해야 합니다.

 

 


정리 - static 특징

  • 클래스가 로딩될 때 Static 영역에 생성됨 -> GC의 관리 대상이 아님
  • 객체를 생성하지 않고도 Static 멤버에 접근 가능 -> 공용 유틸리티 용도로 적합
  • static의 사용은 공용 유틸 함수나 공용으로 쓰일 상수 변수로 주로 쓰임

 

 

참고사이트

https://coding-factory.tistory.com/524

https://vaert.tistory.com/101

'Java' 카테고리의 다른 글

[Java] 컴파일러 vs 인터프리터 vs JIT compiler  (0) 2021.05.31
[Java] JVM 구조와 원리  (0) 2021.05.23
[Java] String, StringBuffer, StringBuilder  (0) 2021.04.22
[Java] JavaAgent  (0) 2021.01.17
[Java] Garbage Collector  (0) 2020.05.11