JAVA/about java

[static] satic 블록, static 변수와 static 메서드

dev_rosieposie 2023. 5. 3. 17:34

Goal 

  1. static 메모리 구조
  2. static 이란?
  3. static 을 사용하는 이유
  4. static 변수란?
  5. static 메소드란?
  6. static 블록이란?

참고

static 블록, static 메소드, static 변수 초기화 시점

 

[Class Loader] 클래스 로딩 시점 / 클래스 로딩 / 클래스 초기화

시작하기 전 ... 싱글턴을 생성하는 방법 중 Eager Initialization의 단점은 인스턴스를 미리 생성하기 때문에, 사용하지 않을 경우 메모리 점유로 인한 자원낭비가 된다였다. 여기서 나의 의문의 시작

dev-rosiepoise.tistory.com

 static  메모리 구조 

static 영역 heap 영역
우리가 만든 클래스 new 연산을 통해 생성한 객체
모든 객체가 공유하는 메모리로 gc의 대상이 아님 gc의 대상이 되어 메모리 관리를 받는다
프로그램 종료시 까지 메모리가 할당된 채로 존재하기 때문에 자주 사용하면 퍼포먼스에 악영향을 끼친다 gc로 인한 삭제 전까지 살아 있음

 

 static 이란?  

Java에서 static 키워드를 사용한다는 것은 어떠한 값이 메모리에 한번 할당되어 프로그램이 끝날 때 까지 그 메모리에 값이 유지된다는 것을 의미한다.

 

 static을 사용하는 이유 

인스턴스를 생성할 경우 각 인스턴스는 독립적이기 때문에 서로 다른 값을 유지한다. static을 사용하면 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기때문에 인스턴스들이 공통적으로 값을 유지해야할 때 사용한다. 

 

 static 변수 

클래스 변수(static variable)는 클래스의 인스턴스와는 별개로 존재하는 변수.

즉, 클래스 변수는 객체를 생성하지 않고도 접근가능하며, 클래스 변수는 "static" 키워드를 사용하여 정의

 

1. static 을 사용하지 않은 경우 

public class HeartCounter {
    int count;

    public HeartCounter(){
        this.count++;
        System.out.println("좋아요 개수 ="+count);
    }

    public static void main(String[] args){
        HeartCounter hc1 = new HeartCounter();
        HeartCounter hc2 = new HeartCounter();
    }
}

 

결과

좋아요 개수 =1
좋아요 개수 =1

=> hc1, hc2 객체가 생성될 때 hc1의 count와 hc2의 count가 서로 다른 메모리를 할당 받게되기 때문에 위와 같은 결과가 나온다.

 

2. static 을 사용한 경우 

public class HeartCounter {
    static int count;

    public HeartCounter(){
        count++;	// 더 이상 객체 변수가 아니므로, this삭제 권장
        System.out.println("좋아요 개수 ="+count);
    }

    public static void main(String[] args){
        HeartCounter hc1 = new HeartCounter();
        HeartCounter hc2 = new HeartCounter();
    }
}

결과

좋아요 개수 =1
좋아요 개수 =2

=> hc1, hc2 객체가 생성될 때 hc1, hc2는 하나의 메모리를 공유하기 때문에, 좋아요 개수가 2가 된다.

 

 static 메소드 

클래스의 인스턴스와는 별개로 존재하는 메서드로, 객체를 생성하지 않고도 호출할 수 있다. 클래스 메서드(static method)는 "static" 키워드를 사용하여 정의된다.

하지만 static 메소드는 클래스 변수만 사용할 수 있다는 단점이 있다. = 즉, static은 객체 생성없이도 사용이 가능하기 때문에 static메서드에는 인스턴스 변수를 사용할 수 없다. 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문이다. 

public class HeartCounter {
    static int count;

    public HeartCounter(){
        count++;
        System.out.println("좋아요 개수 ="+count);
    }
    public static int getCount(){
        return count;
    }
    public static void main(String[] args){
        HeartCounter hc1 = new HeartCounter();
        HeartCounter hc2 = new HeartCounter();

        System.out.println("총 좋아요 개수= "+HeartCounter.getCount());
    }
}

결과

좋아요 개수 =1
좋아요 개수 =2
총 좋아요 개수= 2

=> 객체 생성없이, 바로 접근가능 : HeartCounter.getCount()

 

참고

메서드에서 인스턴스변수를 필요로 한다면 static을 사용하지 않는 것이 좋고 인스턴스 변수가 필요하지 않으면 static을 붙이는 것이 좋다. 메서드 호출 시간이 짧아지기 때문에 효율이 올라간다. 

static을 붙이지 않은 메서드는 실행할 때 메서드를 찾는 과정이 추가로 필요하기 때문에 시간이 더 걸린다. 

 

1. util성 메소드  : 객체 생성 없이 바로 접근할 수 있어 static을 사용 

public class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }

    public static int subtract(int a, int b) {
        return a - b;
    }

    public static int multiply(int a, int b) {
        return a * b;
    }

    public static int divide(int a, int b) {
        return a / b;
    }
}

2. 싱글톤 디자인 패턴  

public class DatabaseConnection {
    private DatabaseConnection() {}

    private static DatabaseConnection instance = null;

    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
}

생성자를 private으로 선언하여 인스턴스화를 방지하고, getInstace() 메소드에서는 instance가 없는 경우 새로 생성하고, 그렇지 않은 경우에는 기존의 instacne를 리턴한다.

 

 static 블록 

객체가 생성되기 전에 한 번만 호출되고, 그 이후에는 호출하려고 해도 호출할 수 없다. 클래스 내에 선언되어 있어야하며, 메소드 내에서는 선언할 수 없다. 또한, static블록은 여러개를 선언할 수 있으며, 선언된 순서대로 차례로 호출되기 때문에 선언 순서는 매우 중요하다.

public class StaticBlock {
    static int data=1;
    public StaticBlock(){
        System.out.println("StaticBlock 생성자");
    }

    static {
        System.out.println("1. static 블록");
        data=3;
    }

    static {
        System.out.println("2. static 블록");
        data=5;
    }

    public static int getData(){
        return data;
    }
}
public class StaticBlockCheck {
    public static void main(String[] args){
        StaticBlockCheck check = new StaticBlockCheck();
        check.makeStaticBlockObject();
    }
    public void makeStaticBlockObject() {
        System.out.println("creating block1");
        StaticBlock block1 = new StaticBlock();
        System.out.println("create block1");
        System.out.println("------------------");
        System.out.println("creating block2");
        StaticBlock block2 = new StaticBlock();
        System.out.println("create block2");
    }
}

결과

creating block1
1. static 블록
2. static 블록
StaticBlock 생성자
create block1
------------------
creating block2
StaticBlock 생성자
create block2 

 

=> 두개의 StaticBlock객체를 만들었지만, static 블록들은 단 한 번씩만 호출됨. 또한 생성자가 호출되기 전에 static 블록들이 호출 됨. 

이 같은 static 블록은 클래스를 초기화할 때 꼭 수행되어야 하는 작업이 있는 경우 유용하게 사용됨.

 

static 초기화 블록이 잘못 사용되면?

static 블록은 해당 클래스가 로딩될 때 딱 한 번 호출되기 때문에, 정확한 실행 순서를 제대로 이해하지 않으면 원하는 동작이 수행되지 않거나 예상하지 못한 동작이 발생할 수 있다.

 

 

 

참고

자바의 신 by 이상민

https://tweety1121.tistory.com/entry/java-static-%EC%82%AC%EC%9A%A9%EC%9D%B4%EC%9C%A0-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-static-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C

https://mangkyu.tistory.com/47

https://wikidocs.net/228

https://velog.io/@lshjh4848/static%EB%B3%80%EC%88%98%EC%99%80-static-%EB%A9%94%EC%84%9C%EB%93%9C-final-xpk2l8e7g0

https://dev-coco.tistory.com/23