static class 와 static method 차이

static class 는 inner class 에서 사용되는 개념으로 Outer class 를 인스턴스화 없이 사용하는 클래스를 (inner) static class 라고 합니다. 그리고 static method 또한 속한 클래스의 인스턴스화 없이 사용되는 메소드를 의미합니다.

예시를 보면 이해가 더 쉽습니다.

class Outer {
	class NonStaticInner { }
    
	static class StaticInner { }
    
   public static void print() { }
}

Outer.NonStaticInner = new Outer.NonStaticInner(); // 컴파일 에러
Outer.StaticInner instance = new Outer.StaticInner();
Outer.print();

그렇다면 여기서 static 은 어떠한 의미를 갖는지 궁금했는데요. 찾아보니 외부 클래스와 상관없이 독립적으로 고정된 클래스/메소드라는 뜻인거 같네요. 

Inner class 는 꼭 static 선언을 해야한다

nested class에 대한 이야기 때문에 static inner class를 접하게 됩니다. Inner 클래스는 꼭 static 으로 선언을 해야하는 이유는 외부참조를 하지 않기 위함입니다. 예를 들어, Inner 클래스를 인스턴스화하는 과정 속에서 Outer 클래스도 같이 인스턴스화를 했습니다. Outer 클래스가 더 이상 필요없어서 가비지 컬렉션이 되길 원했지만 해당 코드는 OOM 에러가 발생합니다.

 

public class Main {
    public static void main(String[] args) {
        int maxSize = 100_000_000;
        List<Outer.NonStaticInner> nonStaticInners = new ArrayList<>();
        int round = 1000;

        for(int i = 0; i < round; i++) {
             nonStaticInners.add(new Outer(new int[maxSize]).new NonStaticInner(new int[1]));
        }

        System.out.println("end");
    }
}

class Outer  {

    int[] oarray;

    Outer(int[] oarray) {
        this.oarray = oarray;
    }

    class NonStaticInner {
        int[] nsi;

        NonStaticInner(int[] nsi) {
            this.nsi = nsi;
        }
    }

    static class StaticInner {
        int[] si;

        StaticInner(int[] si) {
            this.si = si;
        }
    }
}

 

이유는 static 없는 Inner 클래스의 생성자를 디컴파일하면 알 수 있습니다. 알고보니 Outer 클래스에 대한 참조를 갖고 있었습니다. 그래서 Inner->Outer 참조를 갖고 있기 때문에 Outer가 GC되지 않고 OOM이 발생한겁니다.

class NonStaticInner {
	int[] nsi;

	// decompile 하면 외부 클래스였던 Outer 에 대한 참조가 생긴다
	NonStaticInner(final Outer this$0, int[] nsi) {
		this.nsi = nsi;
	}

	public void helloNonStaticInner() {
		System.out.println("hello non static inner");
	}
}

 

그렇기 때문에 Inner 클래스는 외부에 대한 참조를 갖지 않으면 static으로 선언해서 외부 클래스와 상관 없이 독립적으로 고정된 상태로 선언을 해야합니다.

public class Main {
    public static void main(String[] args) {
        int maxSize = 100_000_000;
        List<Outer.StaticInner> staticInners = new ArrayList<>();
        int round = 1000;

        for(int i = 0; i < round; i++) {
            staticInners.add(new Outer.StaticInner(new int[1]));
        }

        System.out.println("end");
    }
}

class Outer  {

    int[] oarray;

    Outer(int[] oarray) {
        this.oarray = oarray;
    }

    static class StaticInner {
        int[] si;

        StaticInner(int[] si) {
            this.si = si;
        }
    }
}

 

클래스 안에 존재하는 enum은 static 선언을 해줘야할까요?

inner 클래스는 static 를 선언해야 하는데 enum도 static 선언을 해줘야 하는지에 대해 공식 문서에 언급된 내용이 있습니다. 원래도 암묵적으로 static이기 때문에 굳이 다시 붙일 이유는 없다고 하네요.

 

https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9:~:text=A%20nested%20enum%20type%20is%20implicitly%20static.%20It%20is%20permitted%20for%20the%20declaration%20of%20a%20nested%20enum%20type%20to%20redundantly%20specify%20the%20static%20modifier

 

마무리

static 이 어떠한 의미인지 알아보는 시간을 보냈습니다. 결국엔 외부 클래스 인스턴스화 없이 독립된 상태가 핵심인거 같네요!