[Java] 내부 클래스

박채은·2022년 11월 4일
0

Java

목록 보기
12/30

내부 클래스

  • 외부 클래스와 내부 클래스가 서로 연관되어 있을 때 사용한다.

장점

  1. 외부 클래스 멤버들에 모두 접근할 수 있다.
  2. 외부적으로 불필요한 데이터를 감출 수 있다.(캡슐화)

종류

  • 멤버 클래스: 내부 클래스 + 정적 내부 클래스
  • 지역 내부 클래스

class Outer { // 외부 클래스
	
	class Inner {
		// 인스턴스 내부 클래스	
	}
	
	static class StaticInner {
		// 정적 내부 클래스
	}

	void run() {
		class LocalInner {
		// 지역 내부 클래스
		}
	}
} 

종 류선언 위치사용 가능한 변수
(static의 유무)
인스턴스 내부 클래스
(instance inner class)
외부 클래스의 멤버변수 선언위치에 선언
(멤버 내부 클래스)
외부 인스턴스 변수, 외부 전역 변수
(외부 클래스에 모두 접근 가능)
정적 내부 클래스
(static inner class)
외부 클래스의 멤버변수 선언위치에 선언
(멤버 내부 클래스)
외부 전역 변수
지역 내부 클래스
(local inner class)
외부 클래스의 메서드나 초기화블럭 안에 선언외부 인스턴스 변수, 외부 전역 변수
익명 내부 클래스
(anonymous inner class)
클래스의 선언과 객체의 생성을 동시에 하는 일회용 익명 클래스외부 인스턴스 변수, 외부 전역 변수

멤버 내부 클래스

  • 외부 클래스의 멤버로 존재
  • 내부 클래스는 외부 클래스에 모두 접근이 가능하다.
    • 하지만 static이 붙은 정적 내부 클래스의 경우에는 static 변수/메서드만 가능하다.

인스턴스 내부 클래스

  • non- static
  • 내부에 static 변수는 사용할 수 없다.
  • 외부 클래스의 모든 접근 지정자 멤버에 접근할 수 있다.
  • 반드시 외부 클래스 인스턴스를 생성한 이후에, 내부 클래스 객체를 생성할 수 있다.
    => 외부 클래스 이름만으로는 생성할 수 없고, 외부 클래스의 인스턴스로 생성할 수 있다.
class Outer { //외부 클래스
    private int num = 1; //외부 클래스 인스턴스 변수
    private static int sNum = 2; // 외부 클래스 정적 변수

    private InClass inClass; // 내부 클래스 자료형 변수 선언

    public Outer() { //외부 클래스 생성자
        inClass = new InClass(); // 내부 클래스 인스턴스를 생성
    }

    class InClass { //인스턴스 내부 클래스
        int inNum = 10; //내부 클래스의 인스턴스 변수

        void Test() {
            System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
            System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
        }
    }

    public void testClass() {
        inClass.Test();
        // 외부 클래스에서 내부 클래스 인스턴스를 사용하지 않고 접근 불가!
        // Test();
        // inNum = 5;
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        System.out.println("외부 클래스 사용하여 내부 클래스 기능 호출");
        outer.testClass(); // 내부 클래스 기능 호출
    }
}

// 출력값

외부 클래스 사용하여 내부 클래스 기능 호출
Outer num = 1(외부 클래스의 인스턴스 변수)
Outer sNum = 2(외부 클래스의 정적 변수)

✅ 위의 코드를 통해서, 외부 클래스에서 내부 클래스에 접근하는 방법을 알 수 있다.

1) 외부 클래스에서 내부 클래스의 인스턴스를 생성
2) 내부 클래스 인스턴스에 포인트 연산자를 사용하여, 내부 클래스의 멤버 변수에 접근할 수 있다.

외부 클래스에서 내부 클래스 인스턴스를 사용하지 않고, 직접 내부 클래스의 멤버에는 접근할 수 없다!


정적 내부 클래스

  • static
  • 일반적으로 class에는 static을 붙이지 못하지만 Inner 클래스에는 붙일 수 있다.
  • 외부 클래스의 모든 접근 지정자의 멤버에 접근할 수 있다.
  • 외부 클래스 인스턴스가 없어도 내부 클래스의 객체를 생성할 수 있다.
    => 외부_클래스_이름.내부_클래스_이름 = new 외부_클래스_이름.내부_클래스_이름()
class Outer { //외부 클래스
    private int num = 3; //내부 클래스의 인스턴스 변수
    private static int sNum = 4;

    void getPrint() {
        System.out.println("인스턴스 메서드");
    }

    static void getPrintStatic() {
        System.out.println("스태틱 메서드");
    }

    static class StaticInClass { // 정적 내부 클래스
        void test() {
            System.out.println("Outer num = " +sNum + "(외부 클래스의 정적 변수)");
            getPrintStatic();
            // num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.StaticInClass a = new Outer.StaticInClass(); //정적 이너 클래스의 객체 생성
        a.test();
    }
}

//출력값
Outer num = 4(외부 클래스의 정적 변수)
스태틱 메서드

선언 방식 비교

class Outer { //외부 클래스
    private int num = 3; //내부 클래스의 인스턴스 변수
    private static int sNum = 4;

    void getPrint() {
        System.out.println("인스턴스 메서드");
    }

    static void getPrintStatic() {
        System.out.println("스태틱 메서드");
    }
    class inClass { // 인스턴스 클래스
        void test() {
            System.out.println("Outer num = " +num + "(외부 클래스의 인스턴스 변수)");
            getPrint();
        }
    }
    static class StaticInClass { // 정적 내부 클래스
        void test() {
            System.out.println("Outer num = " +sNum + "(외부 클래스의 정적 변수)");
            getPrintStatic();
            // num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
        }
    }
}

public class Main {
    public static void main(String[] args) {
        //정적 이너 클래스의 객체 생성
        Outer.StaticInClass a = new Outer.StaticInClass();
        a.test();

        // 인스턴스 클래스의 객체 생성
        Outer o = new Outer();
        Outer.inClass b = o.new inClass();
        // 다른 방법
        Outer.inClass b = new Outer().new inClass();
        b.test();
    }
}
// 출력
Outer num = 4(외부 클래스의 정적 변수)
스태틱 메서드
Outer num = 3(외부 클래스의 인스턴스 변수)
인스턴스 메서드

정적 내부 클래스: Outer.StaticInClass a = new Outer.StaticInClass();
⭐ 인스턴스 내부 클래스: Outer.inClass b = new Outer().new inClass();

✅ 외부 클래스의 인스턴스를 생성한 후에, 외부 클래스 인스턴스 안에 내부 클래스 인스턴스가 만들어지므로, 인스턴스 내부 클래스는 new 연산자를 2번 사용해야 한다.


지역 내부 클래스

  • 메서드 내에 정의되는 클래스(클래스의 멤버가 아니다!)
  • 메서드 내부에서만 사용 가능 => 일반적으로 메서드 안에서 선언하고 객체를 생성해서, 바로 사용한다!
class Outer { //외부 클래스
    int num = 5;
    void test() {
        int num2 = 6;
        class LocalInClass { //지역 내부 클래스
            void getPrint() {
                System.out.println(num);
                System.out.println(num2);
            }
        }
        LocalInClass localInClass = new LocalInClass();
        localInClass.getPrint();
    }
}
public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test();
    }
}

//출력값
5
6

Java의 내부 클래스는 static으로 선언하자!

https://johngrib.github.io/wiki/java-inner-class-may-be-static/

내부 클래스가 외부 클래스의 멤버를 참조하지 않는다면, 내부 클래스를 static으로 선언하자.

다음과 같은 3가지 이유가 있다.

  1. static이 아닌 경우, 외부 클래스 인스턴스를 통해서만 객체를 생성할 수 있기 때문에 독립적으로 존재할 수가 없다.

  2. static이 아닌 내부 클래스의 인스턴스는 외부 클래스의 인스턴스와 암묵적으로 연결된다.
    이때 내부 클래스의 인스턴스 안에 외부 클래스로의 외부 참조를 갖게 된다.
    그 외부 참조는 메모리를 차지하며 생성도 느리다.(시간과 공간이 낭비)

  3. 외부 클래스 인스턴스 참조를 내부 클래스가 가지고 있기 때문에, 가비지 컬렉션이 외부 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수도 있다.
    (외부 클래스를 더 이상 사용하지 않지만, 내부 클래스의 참조로 인해서 GC가 수거하지 못할 수도 있기 때문에)


참고-1
참고-2
참고-3
참고-4
참고-5
정적/인스턴스 내부 클래스 차이점

0개의 댓글