멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 바깥 클래스의 필드와 메소드에 사용 제한이 생긴다.
B
는 field1
)의 초기값이나 인스턴스 메소드(method1()
)에서 객체를 생성할 수 있으나, field3
)의 초기값이나 정적 메소드(method2()
)에서는 객체를 생성할 수 없다.C
는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.public class A {
//인스턴스 필드
B field1 = new B(); //(o)
C field2 = new C(); //(o)
//인스턴스 메소드
void method1() {
B var1 = new B(); //(o)
C var2 = new C(); //(o)
}
//정적 필드 초기화
//static B field3 = new B(); //(x)
static C field4 = new C(); //(o)
//정적 메소드
static void method2() {
//B var1 = new B(); //(x)
C var2 = new C(); //(o)
}
//인스턴스 멤버 클래스
class B { }
//정적 멤버 클래스
static class C { }
}
멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라
멤버 클래스 내부에서 바깥 클래스의 필드와 메소드를 접근할 때에도 제한이 따른다.
B
안에서는 바깥 클래스의 모든 필드의 초기값이나 모든 메소드에서 접근할 수 있다. C
안에서는 field2
)와 메소드(method2()
)에만 접근할 수 있고, field1
)나 메소드(method1()
)는 접근할 수 없다.public class A {
int field1;
void method1() { }
static int field2;
static void method2() { }
class B {
void method() {
field1 = 10;
method1();
field2 = 10;
method2();
}
}
static class C {
void method() {
//field1 = 10; //인스턴스 필드에 접근할 수 없다.
//method1(); //인스턴스 메소드에 접근할 수 없다.
field2 = 10;
method2();
}
}
}
로컬 클래스 내부에서는 바깥 클래스의 필드나 메소드를 제한 없이 사용할 수 있다.
문제는 메소드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때이다.
로컬 클래스의 객체는 메소드의 실행이 끝나도 힙 메모리에 존재하여 계속 사용될 수 있는데, 매개 변수나 로컬 변수는 메소드의 실행이 끝나면 스택 메모리에서 사라지기 때문에 로컬 객체에서 사용할 경우 문제가 발생한다.
자바에서는 이러한 문제를 해결하기 위해 컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해 두고 사용한다. 그리고 매개 변수나 로컬 변수가 수정되어 값이 변경되면 로컬 클래스에 복사해 둔 값과 달라지는 문제를 해결하기 위해 매개 변수나 로컬 변수를 final
로 선언해서 수정을 막는다.
💡 즉, 로컬 클래스에서 사용 가능한 것은
final
로 선언된 매개 변수와 로컬 변수뿐!
자바 7 이전까지는 final
키워드 없이 선언된 매개 변수와 로컬 변수를 로컬 클래스에서 사용하면 컴파일 에러가 발생했지만, 자바 8 부터는 컴파일 에러가 나지 않는다. 그렇다고 final
이 아닌 매개 변수와 로컬 변수를 허용한다는 것은 아니다! final
선언을 하지 않아도 여전히 값을 수정할 수 없는 final
특성을 갖는다.
final
키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치이다.
final
키워드가 있다면 로컬 클래스의 메소드 내부에 지역 변수로 복사되지만,final
키워드가 없다면 로컬 클래스의 필드로 복사된다.클래스 내부에서 this
는 객체 자신의 참조이다. 중첩 클래스에서 this
키워드를 사용하면 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 된다.
중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스의 이름
을 this
앞에 붙여주면 된다.
바깥클래스.this.필드
바깥클래스.this.메소드();
Outter
- 중첩 클래스에서 바깥 클래스 참조 얻기public class Outter {
String field = "Outter-field";
void method() {
System.out.println("Outter-method");
}
class Nested {
String field = "Nested-field";
void method() {
System.out.println("Nested-field");
}
void print() {
// 중첩 객체 참조
System.out.println(this.field);
this.method();
// 바깥 객체 참조
System.out.println(Outter.this.field);
Outter.this.method();
}
}
}
OutterExample
- 실행 클래스public class OutterExample {
public static void main(String[] args) {
Outter outter = new Outter();
Outter.Nested nested = outter.new Nested();
nested.print();
}
}
Nested-field
Nested-method
Outter-field
Outter-method
이것이 자바다 책