java -verbose:class 클래스명.java
명령어로 확인해보자 : 클래스 로딩 디버거public class test {
public static void main(String[] args) {
}
}
class Temp {
}
[ 실행 결과 ]
Temp
클래스가 로드되지 않는 결과가 나온다.public class test {
public static void main(String[] args) {
Temp tmp = new Temp();
}
}
class Temp {
}
[ 실행 결과 ]
Temp
클래스가 로드된 것을 확인할 수 있다.Q) 위와 같은 결과가 나온 이유
클래스가 로딩되는 방식은 크게 2가지가 있다.
- 명시적 로딩 :
import
키워드 사용하여 클래스를 명시적으로 로드한다.- 암시적 로딩 : 클래스 인스턴스를 생성하거나(
new
)하거나
클래스의 정적 멤버에 접근할 때 암시적으로 로드된다.
- 첫 번째 코드 :
Temp
클래스를 명시적으로 로드하지 않기 때문에 암시적 로딩에 의존하게 된다.
하지만,Temp
클래스의 인스턴스를 생성하거나 정적 멤버에 접근하지 않았기에 암시적 로딩마저 일어나지 않아,Temp
클래스는 로드되지 않는다.- 두 번째 코드 :
Temp
클래스의 인스턴스를 생성하기 때문에 암시적 로딩이 일어나Temp
클래스가 로드된다.
public class test {
public static void main(String[] args) {
String value = Temp.value;
}
}
class Temp extends Parent{
static String value="value";
}
class Parent {
}
[ 실행 결과 ]
Parent
)를 먼저 로드하고, 자식 객체(Temp
)를 그 다음 로드하는 결과가 나온다.Q) 왜 부모 클래스가 먼저 로드되는 걸까?
자바 상속의 특징 중 일부를 확인해보자
- 자식은 부모의 요소를 전부 가져온다. (단, 생성자는 제외)
- 상속받지 않는 클래스들은 모두 최상위 클래스
java.lang.Object
클래스를 묵시적으로 상속받는다.
즉, 자식 클래스(Temp
)는 부모 클래스인 (Parent
) 클래스를 먼저 로드하게 된다.
※ static 변수로 선언된 모든 자원들은 클래스 로딩시 생성되고, JVM이 종료되면 그 값은 메모리에서도 종료된다.
public class test {
public static void main(String[] args) {
String value = Temp.value;
}
}
class Temp extends Parent{
static final String value="value";
}
class Parent {
}
[ 실행 결과 ]
Temp
클래스가 로드되지 않음에 따라 부모 클래스(Parent
) 또한 로드되지 않는 결과가 나온다.Q) 위와 같은 결과가 나온 이유
static final String value
는 컴파일 타임 상수
즉, 런타임 때 클래스 로더를 통해 로딩되지 않아도 그 값을 알 수 있는 값이기 때문이다.
public class test {
public static void main(String[] args) {
Temp.getInstance();
}
}
class Temp{
static void getInstance() {
}
}
[ 실행 결과 ]
Temp
클래스가 로드되었다.public class test {
public static void main(String[] args) {
System.out.println(new Holder().new Inner());
}
}
class Holder {
class Inner {
}
}
[ 실행 결과 ]
Holder
클래스와 Holder
클래스의 내부 클래스(Inner
)가 로드되었다.Q) 위와 같은 결과가 나온 이유
외부 클래스(
Holder
)의 내부 클래스(Inner
)를 사용하기 위해선 외부 클래스를 먼저 인스턴스화해야 하기에Holder
클래스가 먼저 로드되고, 그 다음으로Inner
클래스가 로드된다.
public class test {
public static void main(String[] args) {
Holder.Inner inner = new Holder.Inner();
System.out.println(inner);
}
}
class Holder {
static class Inner {
}
}
[ 실행 결과 ]
Holder
클래스가 로드되지 않았다.Q) 5)와 달리 6)의 결과가 나온 이유
static 클래스는 static 변수나 static 메서드 등 처럼 JVM 동작 시, 한 번만 로드되는 개념으로 받아드려선 안 된다.
아래 예시를 보았을 때, static 참조 변수static String str
은 실행되면 한 번만 로드되기에str1
과str2
는 같은 객체를 가리키게 된다.
또한, 클래스는 객체로 만들 때마다 메모리의 다른 위치에 저장되기에Inner inner1
과Inner inner2
는 각자 다른 객체를 가리키며false
가 나오게 된다.
이러한 메커니즘대로라면static class
의 참조 변수는 같은 객체를 가리켜true
가 나온다고 생각할 수 있지만, 결과적으로false
가 나온다.
이 말은 즉슨,static class
는static
변수와 달리 동작 시, 한 번만 로드되는 개념이 아니라는 것이다.
public class test {
static String str = new String("Hello");
static class InnerStatic {
}
class Inner {
}
public static void main(String[] args) {
String str1 = str;
String str2 = str;
System.out.println(str1 == str2);
test.Inner inner1 = new test().new Inner();
test.Inner inner2 = new test().new Inner();
System.out.println(inner1 == inner2);
InnerStatic tmp1 = new InnerStatic();
InnerStatic tmp2 = new InnerStatic();
System.out.println(tmp1 == tmp2);
}
}
// true
// false
// false
public class test {
public static void main(String[] args) {
System.out.println(Holder.Inner.num);
}
}
class Holder {
static class Inner {
static int num=1;
}
}
[ 실행 결과 ]
static
변수를 갖는 Holder
의 내부 클래스 Inner
만 로드되는 결과를 볼 수 있다.Q) 만약, 6번을 설명하기 위해 사용한 예시의
InnerStatic
처럼static
내부 클래스에static String str=new String("hello");
를 넣고 이를 참조하는 2개의 객체 변수를 비교했을 때 결과값은 무엇이 나올까?
A) static 클래스가 아니기에 한 번만 로드된다. 때문에 2개의 변수는 같은 객체를 참조하게 된다.
처음 시작할 땐 이렇게 자세히까지 알아야하나 싶었는데 확실히 코드에 적용시켜 공부하니 중요한 내용이었음을 깨닫게 되었다.
"세상에 나쁜 공부는 없다" 오늘도 1승 획득