[이것이자바다] Chapter 09. 중첩 선언과 익명 객체

kims·2023년 12월 27일
0

이것이자바다

목록 보기
9/9
post-thumbnail

9.1 중첩 클래스

  • 클래스 내부에 선언한 클래스
  • 중첩 클래스를 사용하면 클래스의 멤버를 쉽게 사용할 수 있고 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
  • 중첩 클래스도 컴파일하면 바이트코드가 별도로 생성된다.

9.2 인스턴스 멤버 클래스

  • A 객체를 생성해야만 B 객체를 생성할 수 있다.
  • 클래스 내부에는 일반 클래스와 같이 필드, 생성자, 메서드 선언이 올 수 있다.
  • Java 17부터 정적 필드와 정적 메서드 선언이 가능하다.
package ch09.sec02.ex;

public class A {
    class B {
        int field1 = 1;

        static int field2 = 2;

        B() {
            System.out.println("B 생성자 실행");
        }

         void method1() {
             System.out.println("B-method1() 실행");
         }

         static void method2() {
             System.out.println("B-static-method2() 실행");
         }
    }
}

package ch09.sec02.ex;

public class Main {
    public static void main(String[] args) {
        A.B.method2();  // B-static-method2() 실행

        A a = new A();
        A.B b = a.new B();  // B 생성자 실행

    }
}

9.3 정적 멤버 클래스

  • C 객체를 생성하지 않아도 D 객체를 생성할 수 있다.
  • 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다.
  • 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미로 쓰인다.
  • 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static 키워드를 붙여서 정적 멤버 클래스로 만든다.
    왜냐하면, 비정적 멤버 클래스는 바깥 클래스의 인스턴스와 암묵적으로 연결되어 바깥 인스턴스의 메서드를 호출하거나 참조를 가져올 수 있다.
    이 참조를 저장하려면 시간과 공간이 소비되어 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못해 메모리 누수가 발생할 수 있다.
package ch09.sec02.ex;

public class C {
   static class D {
       D() {
           System.out.println("D-생성자 실행");
       }
   }

   // 인스턴스 필드
    D field1 = new D();

   // 정적 필드
    static D filed2 = new D();

    C() {
        System.out.println("C-생성자 실행");
        D d = new D();
    }

    void method1() {
        D d = new D();
    }

    static void method2() {
        System.out.println("method2() Call!");
        D d = new D();
    }

}


package ch09.sec02.ex;

public class Main {
    public static void main(String[] args) {
//        C.method2();                // D-생성자 실행
//        C.D filed2 = C.filed2;      // D-생성자 실행

        /*
            D-생성자 실행
            D-생성자 실행
            C-생성자 실행
            D-생성자 실행
        */
        C.D fieldA = new C().field1;    // 정적 필드 초기화 -> 인스턴스 필드 초기화 -> 생성자 호출

        System.out.println("-----");
        
        /*
            D-생성자 실행
            C-생성자 실행
            D-생성자 실행
        */
        C.D fieldB = new C().field1;    // 인스턴스 필드 초기화 -> 생성자 호출

    }
}
  • 클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있다.

💡 클래스 로딩 시점

  • 클래스의 인스턴스가 생성될 때
  • 클래스의 정적 변수가 사용될 때
  • 클래스의 정적 메서드가 호출될 때

출처👉클래스는 언제 로딩되고 초기화되는가? (feat. 싱글톤)

9.4 로컬 클래스

  • 생성자 또는 메서드 내부에서 선언된 클래스
  • 로컬 클래스는 생성자와 메서드가 실행될 동안에만 객체를 생성할 수 있다.
  • 내부에 필드, 생성자, 메서드 선언이 올 수 있고, Java 17부터는 정적 필드와 정적 메서드를 지원한다.
  • 로컬 변수는 Java 8 이후부터는 명시적으로 final 키워드를 붙이지 않아도 final 특성을 가지고 있어 값을 읽을 수만 있고 수정할 수 없다.
package ch09.sec02.ex;

public class E {
    public E() {
        System.out.println("E 객체 생성");
    }

    void method() {

        int var = 1;
        
        class F {
            int field = 1;
            public F() {
                System.out.println("F 객체 생성");
            }

            void method1() {
                // var = 2;    // Variable 'var' is accessed from within inner class, needs to be final or effectively final
                field = 2;
                System.out.println("F객체 field 값: " + field);
            }

            static void method2() {
                System.out.println("F객체 static 메서드 실행!");
            }
        }

        F f = new F();
        f.method1();

        F.method2();
    }
}


package ch09.sec02.ex;

public class Main {
    public static void main(String[] args) {
        E e = new E();  // E 객체 생성
        System.out.println(" --- ");
        e.method();     // F 객체 생성
    }
}


💡참고

profile
기술로 세상을 이롭게

0개의 댓글