Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

우주코딩

inner 클래스 사용, 클래스 import, 로컬 클래스, 로컬 클래스와 로컬 변수 본문

자바 본 강의

inner 클래스 사용, 클래스 import, 로컬 클래스, 로컬 클래스와 로컬 변수

우주코딩 2021. 8. 17. 20:14

inner 객체 레퍼런스

inner 객체 만들 때 바깥 객체 클래스 주소를 가지고 만들어야한다.
바깥클래스 객체 주소가 이너 객체 내부에 보관된다
바깥클래스명.this로 저장된다는 것이다.

inner 클래스 사용

inner 클래스의 객체를 생성하려면 반드시 바깥 클래스의 인스턴스 주소가 필요하다.
바깥객체.new inner객체();

인스턴스 메서드를 호출하려면 반드시 해당 클래스의 인스턴스 주소가 필요하다.

class C {
    static m1(){};
    m2(X obj = this.new X();){}; // 이 this. 는 C 클래스를 뜻한다.
    class X{
        test(){};
    }
}

C obj = new C();
obj = new X(); (앞에 this가 생략된 것)
obj = new this. new X();

스태틱 메서드는 바깥 클래스를 담는 this라는 변수가 없어서 인스턴스 생성 불가능하다.

클래스 import

중첩 클래스를 직접 import 할 수 있다.
import가 하는 일은 클래스를 로딩하는 일이 아니다! 컴파일러에게 클래스의 위치를 알려주는 것이다!

%% 클래스 로딩할 때%%
레퍼런스 선언시엔 클래스 로딩하지 않는다. 클래스의 스태틱 멤버를 사용하려할 때 로딩이 안 되어 있으면 로딩하는 것이다.
또는 인스턴스를 생성하려면 클래스 정보를 알아야 할 때, 생성자를 호출할 때, 클래스를 로딩하는 것이다. forName이라는 메서드를 사용해서 강제적으로 로딩하려 할 때

inner 클래스 주소 전달

inner class 의 인스턴스를 생성할 때 바깥 클래스의 객체 주소를 어떻게 전달할까?

  • 컴파일러는 위의 기본 생성자 호출 코드를 바깥 클래스의 객체 주소를 받는 생성자 호출 코드로 변경한다.
  • .class 파일에서 outer.new X() 코드에 해당하는 부분
  • 컴파일러는 모든 생성자에 바깥 클래스의 객체 주소를 받는 파라미터를 추가한다.
Class F{
    Class X{
X(int a) {} => 컴파일러는 다음 코드로 변경한다. 
X(F arg0, int a) {}

X(String s, int a) {} => 즉 컴파일러는 다음 코드로 변경한다.
X(F arg0, java.lang.String s, int a) {}

    }
}

로컬 클래스

메서드 안에 선언된 클래스를 로컬클래스라고 한다.
특정 메서드안에서만 사용된다.
쓸데없이 외부로 노출하지 않기 위함.
노출을 줄이면 유지보수에 좋다.

클래스 블록과 .class 파일

로컬 클래스는 메서드를 호출할 때 클래스가 정의되는 것이 아니다.
컴파일이 끝나면 클래스 안에 있는 클래스는 밖으로 뽑혀 나온다.


class A{ //A.class
    static class X{} //A$X.class 제약조건 : A의 스태틱
    class Y{} //A$Y.class 제약조건: A의 인스턴스
    void m1(){ 
        class Z{} //A$1Z.class 제약조건 : A.m1()
    }
}

$1 은 첫번째 로컬 클래스라는 의미이다.
메서드를 호출할 때 클래스가 로딩되는 것이다. 이미 로딩이 되어있다면 다시 로딩하지 않는다.

로컬클래스와 바깥클래스의 인스턴스

class A{

    void m1(){ // 인스턴스 메서드 => 바깥 클래스의 인스턴스 주소가 있다. this.
        class X{} // 인스턴스 메서드의 로컬클래스
    }

    static void m2(){ // 스태틱 메서드 => 바깥클래스의 인스턴스 주소가 없다.
        class X{} // 스태틱 메서드의 로컬클래스
    }
}

컴파일하면 A.class 만들어지고 X라는 클래스 파일이 A$1X.class로 만들어진다.
두번째 A$2X.class 만들어진다.

A$1X.class를 컴파일하면 A outer; , X(A outer){} 가 포함된다.
즉 바깥 클래스의 인스턴스 주소를 저장할 필드와 생성자가 추가된다.

A$2X.class를 컴파일 하면 바깥 클래스의 인스턴스를 모르기 때문에 그와 관련된 생성자를 추가하지 않는다.

로컬 클래스의 인스턴스 생성

class A{

    void m1(){ 
        class X{}
        X obj = new X(); // new X(this) 
    }

    static void m2(){ 
        class X{}
        X obj = new X(); //
    }
}

생성자를 호출할 때 바깥 클래스의 인스턴스 주소(this)를 파라미터로 넘긴다.

스태틱 멤버로 바깥 클래스의 인스턴스 주소를 저장하는 this라는 변수가 없기 때문에 생성자는 호출할 때 넘겨주지 않는다.

로컬클래스명

Class B2 {
    void m1(){
        class X { } //B2$1X
        class Y { } //B2$1Y
        }

    static void m2(){
        class Y{} //B2$2Y
        class X{} //B2$2X
        class Z{} //B2$1Z
    }
}

로컬 클래스와 로컬 변수

로컬 클래스는 로컬 변수를 직접 사용할 수 있다!

메서드에 선언된 로컬 변수 접근하기

class D3 {
  void m1() {
    final int v1 = 1;
    int v2 = 2;
    int v3 = 3;
    v3 = 30;

    class X {
      void f() {
   // 로컬 클래스에서는 바깥 메서드의 로컬 변수를 사용할 수 있다.
    //1) final 로 선언된 경우
        System.out.printf("v1 = %d\n", v1);

    // 2) final 로 선언된 것은 아니지만 값을 한 번만 할당한 경우.
        System.out.printf("v2 = %d\n", v2);

    // => 값을 여러 번 할당한 경우에는 접근할 수 없다.
    System.out.printf("v3 = %d\n", v3); // 컴파일 오류!

파이널이거나 결과적으로 파이널과 같을 경우 로컬 변수에 접근 가능하다.
값을 한번만 할당해야하는 이유는 로컬 클래스의 객체가 사용하는 로컬 변수는 메서드 호출이 끝났을 때 제거되기 때문이다. 즉, 로컬 클래스에서 바깥 메서드의 로컬 변수를 사용할 경우에는 값을 조회하는 용도로 사용하는 것이다.

Comments