글 작성자: beaniejoy

 지난번에 접근한정자 파트에서 Static과 관련한 내용을 다룬적이 있었다. 

- 다시한번 정리하자면

1) 클래스 내에 static 변수(메서드)를 사용하면 클래스 변수(메서드)라 하고 클래스를 통해 생성되는 모든 객체들이 이 변수값을 공유한다.

2) 모든 객체들이 공유하는 만큼 객체이름을 통해 해당 static 변수(메서드)에 접근가능하지만 클래스 이름을 통해서 접근할 것을 권장한다.

3) static 초기화는 단 한번만 작동하며, static 블럭 안에는 static 변수(메서드)만 가능하며 객체가 생성되기도 전에 가장 먼저 실행된다.

 

 혹시 이해가 안가는 부분이 있으면 [Java] Access Modifier - 접근한정자에 대한 공부를 참고하면 좋을 것 같다.

 

이번 시간에는 Static에 대해 좀 더 자세히 알아보고자 한다. 강의 시간에 배우고 속으로 유레카를 외쳤던 만큼 값진 배움을 이 곳에 정리하면서 머릿속에 영원히 저장할 생각이다.


Static 초기화 복습 

 우선 저번 게시글에서도 다뤘던 부분을 다시 끄집어내보고 싶다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StaticInitializationDemo {
    int age; // instance variable
    static String title; // static variable
 
    // static initialization
    // 가장 먼저 호출이 된다.
    // 먼저 메모리에 올라온다. 그래야 main에서 사용
    // 딱 한번 호출, 초기화됨
    static {
        System.out.println("static block");
        title = "모기지론";
    }
 
    // 생성자 주의사항
    // 생성자를 만드는 순간 default 생성자 사용시 오류 발생가능
    // default 생성자를 따로 설정해야 한다.
    // 생성자의 접근 한정자는 객체 생성과 관련있다.(private으로 할경우)
    public StaticInitializationDemo(int age) {
        this.age = age; // instance variable 초기화
        System.out.println("constructor");
    }
 
    public static void main(String[] args) {
        System.out.println("main");
        new StaticInitializationDemo(10);
        new StaticInitializationDemo(20);
        new StaticInitializationDemo(30);
    }
}
 

 static 블럭을 통해 static 변수를 초기화했다. 이 코드를 실행시키는 순간 해당 클래스를 돌리게 되어서 main이 실행되기도 전에 static 블럭이 가장 먼저 실행이 되는 것을 볼 수 있다. 즉 static 초기화 블럭은 객체를 만들어내기위해 해당 클래스를 가져오든 직접 그 클래스를 실행시키든 static을 가지고 있는 그 클래스가 실행이 되는 순간 메모리에 가장 먼저 올라간다. 말이 어려울 수 있는데 다음 예시를 한 번 보자. 강사님께서 올려주신 예시인데 이거보고 static 초기화에 대해 완전히 이해할 수 있었다.


다른 Class에서 실행할 때 Static 초기화는?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MemoryLoad {
    public static String name;
    static {
        name = "BeanieJoy";
        System.out.println("name : " + name);
        m();
    }
 
    public static void m() {
        System.out.println("MemoryLoad - m()");
    }
 
    public MemoryLoad(){
        System.out.println("MemoryLoad Constructor");
    }
}
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MemoryLoadDemo {
    public static void main(String[] args) {
        System.out.println("나는 main이다.");
        try {
            Class.forName("java_20191210.MemoryLoad").newInstance();
            
          
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
 

 MemoryLoadDemo를 실행하면 다음과 같이 결과가 나온다.

 

 

 Class.forName(패키지명을 포함한 클래스명).newInstance()를 하게 되면 해당 클래스에 대한 객체를 생성해준다. 즉 Demo 클래스에 있는 main에서 MemoryLoad 클래스를 객체생성하기 위해 호출한 것이므로 이 때는 "나는 main이다." 라는 문구가 먼저 출력되고 그 다음에 MemoryLoad에 있는 static 블럭이 출력됨을 알 수 있다. Demo 클래스 메인 내에서 MemoryLoad 클래스를 호출한 것이므로 호출한 시점에서 static 블럭이 메모리에 올라온다. 여기서도 다시 한 번 확인할 수 있었던 것은 static 블럭을 담고 있는 클래스가 다른 클래스든 어디서든 호출이 되는 순간 static 블럭을 가장 먼저 메모리에 올린다는 것이다.

 

 그럼 다음 코드를 한 번 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package java_20191210;
 
public class MemoryLoadDemo {
    public static void main(String[] args) {
        System.out.println("나는 main이다.");
        try {
            Class.forName("java_20191210.MemoryLoad").newInstance();
            
            MemoryLoad m = new MemoryLoad();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
 

 달라진 점은 MemoryLoad m = new MemoryLoad(); 부분이 추가되었다는 것이다. 실행을 시켜볼까.

 

 

 빨간 네모박스 부분이 MemoryLoad m = new MemoryLoad(); 이 실행되어 나온 결과부분이다. 여기서도 또 한 번 확인할 수 있었던 것은 static 초기화는 수많은 객체를 생성해도 단 한번만 실행된다는 것이다.


Class.forName에 대한 이해

 

 

 java document에서 Class.forName을 검색하면 나오는 내용이다. 반환값으로 Class를 return해주는데 이게 무슨 얘기인가 한 번 보자.

 

 

 

 사실 위의 두 개의 코드는 어느 정도 같은 의미를 가진다.

Class.forName(클래스명) 부분이 MemoryLoad m과 대응하고 .newInstance() 부분은 new MemoryLoad();와 대응한다. 의미는 둘다 Class를 가져와서 새로운 객체를 생성한다! 이 뜻이다. 하지만 명확한 차이점이 있다. 한 번 확인해보자.

 

1. Class.forName만 실행할 경우

 

 

static 초기화했던 부분은 그대로 출력한다. 즉 Class.forName만 하는 순간 반환을 Class로 하기 때문에 어찌됐든 매개변수값으로 넣었던 MemoryLoad 클래스가 호출이 되어 static 블럭을 메모리에 올린다고 할 수 있다. 

 

2. MemoryLoad m; 만 할경우

 

 

 MemoryLoad m; 만 해줘도 static 블럭이 메모리에 올라올 것처럼 보이지만 그렇지 않다. 사실 m이라는 MemoryLoad 성격의 참조변수만 생기는 것이고 Class를 호출한 것은 아닌 것이다.

 

 

※정리

 

1. Static 초기화는 그것을 담고 있는 클래스가 호출이 되는 순간 가장 먼저 메모리에 올라온다.

2. Class.forName(클래스명)만 사용해도 해당 클래스 안에있는 static블럭은 메모리에 올라온다. 이를 유용하게 이용할 수 있을 것이다.

3. OOP는 공부하면 할 수록 어렵다.

 

 

 

 

 

 

저는 이제 개발을 공부하기 시작한 초보자이기 때문에 많이 부족합니다.

혹시나 틀린 부분이 있으면 바로바로 지적 부탁드리겠습니다. 이러한 지적은 언제나 환영입니다! :)