24. 멤버 클래스는 되도록 static으로 만들라

📝 전체 내용 간단 요약

아이템 24에서는 클래스 안에 클래스를 정의할 때(static 중첩 클래스와 non-static 내부 클래스) 외부 클래스의 데이터를 사용할 필요가 없다면 static 중첩 클래스를 사용해야 한다고 권장. 이유는 static 클래스가 외부 클래스의 인스턴스에 대한 참조를 갖지 않아 메모리를 더 효율적으로 관리할 수 있고, 클래스 간 불필요한 의존성을 최소화하며, 코드의 가독성과 유지보수성을 높이기 때문입니다.


📚 미리 알아야 할 내용 요약

📍 멤버 클래스 (중첩 클래스)

  • 하나의 클래스 안에 또 다른 클래스를 정의할 수 있음

  • 종류는 크게 두 가지:

종류
설명
특징

static 중첩 클래스

외부 클래스의 특정 인스턴스와 독립적으로 존재할 수 있음

외부 클래스 인스턴스 참조 없음, 독립적

non-static 내부 클래스

외부 클래스의 특정 인스턴스에 종속적으로 존재함

외부 클래스의 모든 멤버에 접근 가능

📍 static 키워드

  • static은 클래스 자체에 속한다는 의미

  • 클래스의 특정 객체가 아닌, 클래스 이름으로 직접 접근 가능

  • 모든 객체가 공유하는 값이나 기능을 정의할 때 사용함

예: 자동차의 바퀴 수(static)는 모든 자동차가 공유하지만, 자동차의 색상은 각 자동차마다 다르므로 공유하지 않습니다.


🚩 주의사항

⚠️ non-static 내부 클래스는 메모리 누수를 일으킬 수 있다!

  • non-static 내부 클래스는 자동으로 외부 클래스의 인스턴스(객체)를 참조함

  • 내부 클래스가 존재하는 동안 외부 클래스도 계속 메모리에 남아있게 됨

  • 필요 이상으로 오랫동안 유지되면 메모리 누수가 발생할 수 있음

메모리 누수란? 더 이상 사용하지 않는 데이터가 계속 메모리를 차지하여, 프로그램 성능이 떨어지거나 심하면 충돌을 일으키는 현상입니다.


⭐ 중요한 점

🔑 내부 클래스가 외부 클래스의 데이터를 쓰지 않으면 static으로 선언하자!

  • static 중첩 클래스는 외부 클래스의 인스턴스와 연결되지 않음

  • 따라서 외부 클래스의 인스턴스가 메모리에 남지 않아 메모리 효율성이 높음

  • 클래스 간 불필요한 의존성을 최소화하고, 가독성과 유지보수성을 증가시킴


❌ 잘못된 예제 (불필요한 non-static 클래스)

class Configuration {
    private static final String DEFAULT_SETTINGS_FILE = "default.cfg";

    // 잘못된 예: static 멤버만 접근하는데도 non-static 내부 클래스로 선언
    class SettingsLoader {
        public String loadSettings() {
            return "Loading settings from: " + DEFAULT_SETTINGS_FILE;
        }
    }

    public SettingsLoader getSettingsLoader() {
        return new SettingsLoader();
    }
}

public class Main {
    public static void main(String[] args) {
        Configuration config = new Configuration();
        Configuration.SettingsLoader loader = config.getSettingsLoader();
        System.out.println(loader.loadSettings());
    }
}

🚨 문제점

  • SettingsLoader가 외부 클래스의 static 변수만 필요하지만 non-static으로 선언됨

  • 이로 인해 Configuration 객체에 대한 불필요한 참조가 생겨 메모리 낭비 발생 가능


✅ 좋은 예제 (적절한 static 중첩 클래스 사용)

class StringFormatter {

    // 올바른 예: 외부 클래스의 데이터가 필요 없으므로 static으로 선언
    public static class Utils {
        public static String reverseString(String input) {
            return new StringBuilder(input).reverse().toString();
        }

        public static boolean isPalindrome(String input) {
            return input.equals(reverseString(input));
        }
    }

    public static void main(String[] args) {
        String text = "madam";
        System.out.println("Reversed: " + Utils.reverseString(text));
        System.out.println("Is Palindrome: " + Utils.isPalindrome(text));
    }
}

🚀 장점

  • 외부 클래스의 객체가 필요 없어 메모리를 효율적으로 관리함

  • 외부 클래스와의 관계가 명확해지고 코드의 유지보수가 용이해짐


✅ non-static 내부 클래스가 꼭 필요한 좋은 예제

import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;

class StringList {
    private List<String> strings = new ArrayList<>();

    public void add(String s) {
        strings.add(s);
    }

    public Iterator<String> iterator() {
        return new StringIterator();
    }

    // 적절한 non-static 내부 클래스 사용 (외부 인스턴스의 데이터에 접근해야 함)
    private class StringIterator implements Iterator<String> {
        private int currentIndex = 0;

        @Override
        public boolean hasNext() {
            return currentIndex < strings.size();
        }

        @Override
        public String next() {
            return strings.get(currentIndex++);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        StringList list = new StringList();
        list.add("Hello");
        list.add("World");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

🚀 장점

  • StringIterator는 외부 클래스(StringList)의 데이터를 직접 사용하기 때문에 non-static으로 선언하는 것이 옳습니다.

  • 외부 클래스의 상태를 활용하여 정확한 동작을 구현합니다.


🤔 어려운 점

  • 암시적 참조 개념 이해하기

    • non-static 내부 클래스가 외부 클래스의 객체를 자동으로 참조한다는 개념이 처음에는 이해하기 어려울 수 있습니다.

    • 이는 내부 클래스가 외부 클래스의 모든 멤버를 접근할 수 있도록 자바가 자동으로 연결을 만들어주는 특성입니다.

  • 언제 static을 쓸지 정확히 판단하기

    • 내부 클래스가 외부 클래스의 데이터를 정말 사용하는지 여부를 판단하는 것이 초보자에게는 까다로울 수 있습니다.

    • 내부 클래스가 외부 클래스의 특정 객체의 상태를 사용할 때만 non-static을 써야 합니다.


🗣️ 느낀점

  • 처음에는 static과 non-static의 차이가 사소해 보였지만, 실제로는 메모리 관리와 코드 품질에 큰 영향을 준다는 것을 깨달았습니다.

  • 특히 non-static 클래스가 의도치 않은 메모리 누수를 일으킬 수 있다는 사실이 놀라웠고, 클래스 설계 시 매우 신중해야겠다고 생각했습니다.

  • 앞으로 클래스를 설계할 때는 항상 기본적으로 static을 사용하고, 꼭 외부 객체의 상태가 필요한 경우에만 non-static을 쓰는 습관을 들여야겠다고 느꼈습니다.


🎯 핵심 한 줄 정리 (Nutshell)

🔑 외부 클래스의 데이터를 쓰지 않는다면 멤버 클래스는 static으로 선언하여 메모리 효율과 코드의 유지보수성을 높이자!

Last updated