강좌 Course 1. Part 3. ch6 요약
많은 클래스 중에서도 실제로 그 내용이 실행되는 클래스는 main 클래스다. main 클래스는 객체 생성 없이도 실행될 수 있는데, 그 이유는 실행방식에 있다.
Java 프로그램이 실행되면,
1. JVM이 가장 먼저 시작 클래스를 찾는다.
2. 이때, static 키워드가 붙은 멤버들은 클래스를 사용하는 시점에서 정해진 메모리 공간 (=static zone)에 자동으로 한 번만 로딩된다.
3. JVM이 static zone에서 main() 메서드를 호출한다.
4. 호출된 메서드를 Call Stack Frame Area(=Stack Area, last-in-first-out)에 push하여 동작을 시작한다. 다시 말하면, 호출된 메서드들을 Stack Area에 기계어코드로 바꿔 넣는다는 뜻이다.
실행이 끝난 메서드는 Call Stack Frame Area에서 소멸한다.
public class StaticTest {
public static void main(String[] args) {
int a=10;
int b=20;
hap(a,b) // 에러, main은 static에 있으나 hap 메서드는 static이 아니라 불러올 수 없음
}
public int hap(int a, int b){
int v = a+b;
return v;
}
}
static이 붙은 클래스는 JVM이 자동으로 메모리에 로딩하여 실행된다. 반대로 non-static 멤버는 자동으로 로드되지 않기 때문에 객체를 생성하여 메모리에 로딩해야 한다.
public class NonStaticTest {
public static void main(String[] args) {
int a=10;
int b=20;
// 객체 생성
NonStaticTest st = new NonStaticTest();
int n = st.hap(a,b);
System.out.println(n);
}
public int hap(int a, int b){
int v = a+b;
return v;
}
}
현재 실행하고 있는 클래스 내에서 또 해당 객체를 생성한다는 것이 조금 혼란스러울 수 있는데, 실제로는 메모리에 main이 올라가는 것이라고 생각하면 덜 혼란스럽다. 메모리에 딱 한번 로딩되기 때문에 객체를 생성한다고 해서 main이 한번 더 메모리에 올라가거나 하는 것이 아니다.
non-static 멤버가 객체 생성을 통해 메모리에 올라가면, non-static zone에 로딩된다. 이 때 로딩된 것은 객체가 생성되는 메모리 공간인 Heap Area에 자신이 저장된 위치의 포인터이다.
non-static 메서드는 객체를 생성한 후 호출을 해야 한다는 점에서 인스턴스 메서드라고도 한다.
static 멤버는 클래스를 사용하는 시점에서 자동으로 static-zone에 로딩된다. 따라서 new 생성자 메서드를 사용해 객체를 생성하지 않고 바로 메서드를 호출할 수 있다.
클래스 이름에 바로 호출할 메서드를 붙여 사용할 수 있기 때문에 클래스 메서드라고도 한다.
// 예시: static 클래스
public class MyUtil {
public static int sum(int a, int b){
int ans = a+b;
return ans;
}
}
// 예시: 실행할 main 클래스
import fc.java.model.MyUtil;
public class StaticAccess {
public static void main(String[] args) {
int a = 10;
int b = 20;
int sum = MyUtil.sum(a,b); // 생성자메서드를 사용하지 않아도 된다.
System.out.println(sum);
}
}
// 예시: non-static 클래스
public class MyNSUtil {
public int sum(int a, int b){
int ans = a+b;
return ans;
}
}
// 예시: 실행할 main 클래스
import fc.java.model.MyNSUtil;
public class NonStaticAccess {
public static void main(String[] args) {
int a = 10;
int b =20;
MyNSUtil util = new MyNSUtil();
System.out.println(util.sum(a,b));
}
}
생성자 메서드를 사용해 새로 객체를 생성하여 접근한다.
* GC는 주기적으로 Heap Area를 체크하고, 아직 포인터가 남아있는 객체들에 세대를 매긴다. 포인터가 사라진 객체들은 메모리에서 지우고, 남아있는 객체들에는 세대를 추가하는 식으로 진행되며, GC의 우선순위는 세대가 높은 객체가 된다.
(하단의 포인터가~체크는 GC 내용이다. 왜 저기다가 필기했는지 나도 모르겠다...)
* Runtime Constant Pool은 나중에 API 관련 수강할 때.
어떤 클래스의 모든 멤버가 static 멤버라면, 굳이 객체생성을 할 필요가 없다(물론 그렇게 해도 실행은 문제없이 되긴 한다). 이러한 상황에서 객체생성을 막으면 클래스 이름으로 메서드를 호출하는 방식을 사용하도록 public인 생성자 메서드를 private으로 바꿈으로써 유도할 수 있다.
(따라서 생성자가 반드시 public이라는 말은 잘못된 말이다. System, Math같은 클래스들은 private 생성자를 가지고 있다.)
public class AllStaticTest{
private AllStaticTest(){}
public static int hap(int a, int b){
int v = a+b;
return v;
}
public static int max(int a, int b){
return a>b ? a : b;
}
public static int min(int a, int b){
return a<b ? b : a;
}
Class(클래스): 객체를 모델링하는 도구(설계도)
Object(객체): 클래스를 통해서 선언되는 변수
Instance(인스턴스, 실체): 객체생성에 의해 메모리(Heap Memory)에 만들어진 객체
서로 약간씩 다르지만 비슷한 개념으로, 전부 객체를 나타낸다.
잘 봤습니다. 좋은 글 감사합니다.