우주코딩
inner 클래스 사용, 클래스 import, 로컬 클래스, 로컬 클래스와 로컬 변수 본문
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); // 컴파일 오류!
파이널이거나 결과적으로 파이널과 같을 경우 로컬 변수에 접근 가능하다.
값을 한번만 할당해야하는 이유는 로컬 클래스의 객체가 사용하는 로컬 변수는 메서드 호출이 끝났을 때 제거되기 때문이다. 즉, 로컬 클래스에서 바깥 메서드의 로컬 변수를 사용할 경우에는 값을 조회하는 용도로 사용하는 것이다.
'자바 본 강의' 카테고리의 다른 글
제네릭, 제네릭 레퍼런스, 제네릭 파라미터 (0) | 2021.08.23 |
---|---|
익명 클래스, 익명 클래스의 생성자 호출 (0) | 2021.08.17 |
인터페이스 구현하는 두가지 케이스, 패키지에 직접 소속된 클래스, 중첩 클래스, inner 클래스 (0) | 2021.08.13 |
메서드 명칭, 인터페이스와 추상클래스의 콜라보, default 메서드의 필요성, 인터페이스의 static 메서드, 인터페이스와 추상클래스의 상속 (0) | 2021.08.13 |
인터페이스 상속 (0) | 2021.08.12 |