main class는 왜 객체생성없이 실행이 될까??
지금까지 만들어진 클래스를 사용하려면 객체를 생성하는 과정을 거쳐야만 했다. 그런데 main class는 왜 객체를 생성하는 과정이 없어도 실행이 될까? 그것은 static키워드에 해답이 있다.
Call Stack Frame Area
메서드가 호출되면(Method Call) 호출된 기계어 코드가 push되고 실행되는 메모리 공간
현재 프로그램이 실행되고 있는 상태를 파악할 수 있다.(LIFO구조)
public class StaticTest {
public static void main(String[] args) { // call 1
int a = 10;
int b = 20;
int result = StaticTest.sum(a,b);
System.out.println(result);
}
public static int sum(int a, int b) { // call 2
int v = a + b;
return v;
}
}
해당 클래스의 동작 방식 및 생명주기
1. main Method가 실행시 Method Area(static-zone)에 올라감
2. JVM이 static-zone에서 Method Call
3. 호출된 main()이 Stack Area에 push되어 동작이 실행됨(PC는 main()을 가리킴)
4. int sum의 반환값인 StaticTest.hap(a,b) 호출을 위해 static-zone에 할당
5. Method Call로 인해 hap()메서드가 Stack Area에 push되어 동작이 실행됨(PC가 sum()을 가리킴-LIFO구조의 특성)
6. 동작이 종료된 sum()은 Stack에서 사라짐(PC는 main()을 가리킴)
7. 동작이 종료된 main()은 Stack에서 사라짐
none static메서드는 static-zone에 할당될 수 없기 때문에 따로 메모리로 로딩해야한다. 즉, 객체 생성을 통해 메서드를 메모리에 할당시켜야 한다.
public class NoneStaticTest {
public static void main(String[] args) { // call 1
int a = 10;
int b = 20;
NoneStaticTest test = new NoneStaticTest(); // NoneStaticTest객체생성
int result = test.sum(a,b);
System.out.println(result);
}
public int sum(int a, int b) { // call 2
int v = a + b;
return v;
}
}
stack Area에 올라간 인스턴스변수 test는 heap메모리에 할당된 NoneStaticTest객체 주소를 가리킨다.
NoneStaticTest객체의 hap()메서드는 Method Area의 hap()을 가리킨다.
-> 메서드의 기계어 코드가 따로 Method Area에 할당된다는 의미
요약
- static 메서드는 클래스를 사용하는 시점에 자동으로 static-zone에 로딩된다.
- none static 메서드는 객체 생성을 통해 none static-zone에 로딩된다.
public class StaticAccess {
public static void main(String[] args) {
int a = 10;
int b = 20;
int result = MyUtil.sum(a,b); // static메서드인 sum이 클래스 사용 시점에
//자동으로 static-zone에 올라가있기 때문에 객체를 생성할 필요가 없다.
System.out.println(result);
}
}
public class MyUtil {
public static int sum(int a, int b) {
int v = a + b;
return v;
}
}
static 메서드 접근 방법 : 클래스이름.호출메서드
빈번하게 사용하는 메서드의 경우 객체 생성을 통해 사용하기 보단 static메서드로 관리해 호출한다.
1. Method Area
2. Heap Area Generation
3. Stack Area(Call Stack Frame Area) & PCregister(Native method Area)
4. Runtime Constant Pool(Literal Pool)
어떤 클래스의 모든 멤버 메서드가 static인 경우, 객체 생성 후 메서드를 사용할 수 있고, static-zone에 할당된 메서드를 그대로 호출해서 사용할 수도 있다.
일반적으로 static 메서드는 객체 생성과는 관련이 없기 때문에 객체 생성을 통해 인스턴스 메서드로 사용하기 보단 즉시 호출해서 사용하는 방법이 더 바람직한 방법이다.
public class AllStaticTest {
public static void main(String[] args) {
// static 메서드를 사용하기엔 바람직하지 않은 방법임(인텔리제이에서도 경고를 띄움)
AllStatic st = new AllStatic();
System.out.println(st.sum(10,20));
System.out.println(st.max(10,20));
System.out.println(st.min(10,20));
// static 메서드를 사용하는 바람직한 방법
System.out.println(AllStatic.sum(10,20));
System.out.println(AllStatic.max(10,20));
System.out.println(AllStatic.min(10,20));
}
}
public class AllStatic {
private AllStatic() {} // 생성자의 접근 제어자를 private로 설정해 객체 생성을 막을 수 있음
public static int sum(int a, int b) {
int v = a + b;
return v;
}
public static int max(int a, int b) {
//return a > b ? a : b;
return Math.max(a, b);
}
public static int min(int a, int b) {
//return a < b ? a : b;
return Math.min(a, b);
}
}
생성자의 접근 제어자를 private로 사용해 객체 생성을 못하도록 막을 수 있다.
(생성자는 반드시 public이다 -> 잘못된 개념)
-> System, Math 등의 자바 api는 실제로 private생성자를 가지고 있음
Class : 객체를 모델링하는 도구(설계도, 틀)
public class CarDTO {
public int carSn;
public String carName;
public int carPrice;
public String carOwner;
public int carYear;
public String carType;
public CarDTO() {}
...이하 생략
}
Object : 클래스를 통해 선언되는 변수
CarDTO = car; // 객체변수 선언
객체가 구체적인 실체(대상)을 가리키지 않는 상태(객체 변수)
객체가 서로 구분이 되지 않는 시점
Instance : 객체 생성에 의해 메모리(Heap Memory)에 만들어진 객체
car = new CarDTO(); // 객체 생성 및 초기화
객체가 구체적인 실체(대상)을 가리키는 상태(인스턴스 변수)
객체가 서로 구분이 되는 시점
서로 비슷한 개념으로 모두 객체를 나타내는 용어이다.
평소에 자바를 사용할 때 메모리 관련 개념을 크게 생각하지 않았었는데, 이번에 JVM의 메모리 모델과 자바의 동작방식을 익히면서 JVM의 기본적인 메모리 구조나 할당 방식은 자바를 사용함에 있어 매우 필수적인 개념이라는 것을 체험했다. 특히 static메서드를 제대로 배우지 않아서 잘 사용하지 않았었는데 이번에 static키워드와 메모리의 관계에 대해 어느정도 짚고 넘어간 만큼 앞으로 프로그래밍을 할 때에도 static키워드를 사용해보면서 조금 더 메모리 적으로 생각하고 신경 쓸 수 있도록 클린한 애플리케이션을 만들어보고 싶다.
관련 내용에 대해서 조금 더 깊게 알고싶은 부분이나 더블체크 해야할 사항을 파악해서 시일 내에 글을 수정 & 보충 해야겠다.