정적 클래스 관련해서 질문을 받았다. 내부 클래스와 정적 내부 클래스의 차이는 무엇이고 언제 사용하면 좋을지? 당시에는 잘 떠오르지 않아서 언제 정적 클래스를 활용하면 좋을지 모르겠어서 학습했다.
언제 활용할지를 논하기 전에 내부 클래스와 정적 내부 클래스의 차이를 알아보자. 공식문서 에 따르면 정확한 용어는 각각 Inner Class와 Nested Static Class 다.
public class OuterClass {
String outerField = "Outer field";
static String staticOuterField = "Static outer field";
class InnerClass {
void accessMembers() {
System.out.println(outerField);
System.out.println(staticOuterField);
}
}
static class StaticNestedClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field outerField
// System.out.println(outerField);
System.out.println(outer.outerField);
System.out.println(staticOuterField);
}
}
}
가장 큰 차이는 내부 클래스는 외부 클래스의 필드값을 사용할 수 없다는 것이다. 정적 내부 클래스에서 외부 클래스의 필드값을 사용하려면 외부 클래스 값을 주입 받아서 사용해야한다. 내부 클래스에서는 외부를 참조하고 있기 때문에 바로 사용할 수 있다.
외부 참조가 언제 일어나는지 알아보기 위해서 클래스 로딩 시점을 찾아봤다. JVM은 ClassLoader를 통해서 class를 로딩하고 초기화한다. 클래스가 사용될 때 값이 초기화되어 Runtime Data Area에 올라간다.
class Outer {
// static 변수
static String value = "> Outer 클래스의 static 필드 입니다.";
// static final 상수
static final String VALUE = "> Outer 클래스의 static final 필드 입니다.";
Outer() {
System.out.println("> Outer 생성자 초기화");
}
// static 메서드
static void getInstance() {
System.out.println("> Outer 클래스의 static 메서드 호출");
}
// inner 클래스
class Inner {
Inner() {
System.out.println("> Inner 생성자 초기화");
}
}
// static inner 클래스
static class Holder {
static String value = "> Holder 클래스의 static 필드 입니다.";
static final String VALUE = "> Holder 클래스의 static final 필드 입니다.";
Holder() {
System.out.println("> Holder 생성자 초기화");
}
}
}
해당 클래스를 사용해서 정적 내부 클래스와 내부 클래스의 로딩시점을 보자.
public class Main {
public static void main(String[] args) {
new Outer().new Inner();
}
}
외부 클래스를 생성하고, 내부 클래스를 생성하는 인스턴스화 과정이 2번 필요하다. 그렇기 때문에 Outer, Inner 모두 로드된다.
외부 클래스를 초기화한 뒤 내부 클래스를 초기화하기 때문에 외부 참조를 갖게 된다. 암묵적으로 두 클래스는 연결된다.
외부 클래스는 호출 용도로만 사용되지만 내부 클래스의 참조로 인해 GC가 정상적으로 접근할 수 없기 때문에 데이터를 수거해가지 못한다. 메모리 누수가 발생한다.
public class Main {
public static void main(String[] args) {
new Outer.Holder();
}
}
내부 정적 클래스는 외부 클래스를 생성하지 않고 직접 인스턴스화 가능하다. 외부 클래스는 로드되지도 않고 따라서 외부 참조가 발생하지 않는다.
항상 내부 정적 클래스를 만드는 것이 메모리 누수를 방지할 수 있기 때문에 특별한 이유가 없다면 유리하다고 생각한다. 하지만 외부 클래스의 필드값을 내부 클래스에서 사용해야 한다면 내부 클래스를 사용하는 것이 상황에 따라서 적절할 수도 있을거라고 생각한다.
https://tecoble.techcourse.co.kr/post/2020-11-05-nested-class/
https://johngrib.github.io/wiki/java-inner-class-may-be-static/