중첩 클래스
클래스 안에 클래스를 선언 한 것을 의미한다. 클래스 간에 접근이 용이하다는 장점을 가지고 있다. 클래스 뿐만 아니라 인터페이스 또한 중첩으로 선언할 수가 있다.
중첩 클래스는 선언되는 위치에 따라 멤버클래스와 로컬 클래스로 나뉘게 된다.
멤버 클래스 | 클래스 멤버로 선언되는 클래스 |
인스턴스 멤버 클래스 | A객체를 생성해야만 B 클래스 사용 가능 |
정적 멤버 클래스 | A클래스로 바로 접근 할 수있는 B 클래스 | ||
로컬 클레스 | 생성자 및 메소드 내부에서 선언되는 클래스 |
로컬 클래스 | method() 실행시에만 사용 가능한 B클래스 |
인스턴스 멤버 클래스
class A {
class B {
B() {}; //생성자
int field1; //인스턴스 필드
void method1; //인스턴스 메소드
}
}
정적 멤버 클래스
class A {
static class B {
B(); //생성자
int field1; //인스턴스 필드
static intfield2; //정적 필드
void method1(); //인스턴스 메소드
static void method2(); //정적 메소드
}
}
로컬 클래스
class A {
void method() {
class B {
B();
int field1;
void method1();
}
}
}
중첩 클래스 역시 클래스이기 때문에 컴파일 시 .class의 바이트 코드 파일이 생성된다.
멤버 클래스 - A $ B.class
로컬 클래스 - A $1 B.class
인스턴스 멤버 클래스
인스턴스 필드와 메소드만 선언 가능한 클래스이다. 따라서 정적 필드와 정적 메소드는 선언이 불가능하다.
사용법
외부에서 사용시
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();
내부에서 사용시
class A {
class B {
int field1;
B();
void method1();
}
void methodA() {
B b = new B();
b.field1 = 3;
b.method1();
}
}
정적 멤버 클래스
static으로 선언된 클래스를 말하며 정적 클래스와 마찬가지로 인스턴스 및 정적 필드와 메소드 선언이 가능하다.
인스턴스 클래스와 다르게 외부에서 선언할 시에 A객체를 따로 생성할 필요가 없다.
사용법
A.C c = new A.C();
c.field1 = 3;
c.method();
A.C field2 =3;
A.C.method();
로컬 클래스
메소드 내에 선언된 클래스를 로컬 클래스라고 한다. 접근제한자나 static을 붙일 수 없는데 메소드 내부에서만 사용되므로 접근을 제어할 필요가 없기 때문이다
사용법
class A {
void method() {
class B {
B();
int field1;
void method1();
}
D d = new D();
d.field1 = 3;
d.method1();
}
}
중첩클래스 접근 제한
바깥 필드 및 메소드에서 사용 제한
public class A {
인스턴스 필드
B field1 = new B(); //인스턴스 멤버 클래스 인스턴스 필드로 선언 가능(O)
C field2 = new C(); //정적 멤버 클래스 인스턴스 필드로 선언 가능(O)
정적 필드
static B field3 = new B(); //인스턴스 멤버 클래스 정적 필드로 선언 불가능(X)
static C field4 = new C(); //정적 멤버 클래스 정적 필드로 선언 가능(O)
인스턴스 메소드
void method1(){
B var1 = new B(); //인스턴스 멤버 클래스 인스턴스 메소드에 생성 가능(O)
C var2 = new C(); //정적 멤버 클래스 인스턴스 메소드에 생성 가능(O)
}
정적 메소드
static void method2(){
B var1 = new B(); //인스턴스 멤버 클래스 정적 메소드에 생성 불가능(X)
C var2 = new C(); //정적 멤버 클래스 정적 메소드에 생성 가능(O)
}
중첩 클래스
class B {} //인스턴스 멤버 클래스
static class C {} //정적 멤버 클래스
}
멤버 클래스에서 사용 제한
public class A {
필드
int field1; //인스턴스 필드
static int field2; //정적 필드
메소드
void method1(){}; //인스턴스 메소드
static void method2(){}; //정적 메소드
멤버 클래스
class B { //인스턴스 멤버 클래스
void method() {
field1 = 10; //인스턴스 멤버 클래스 메소드에 인스턴스 필드 사용 가능(O)
method1(); //인스턴스 멤버 클래스 메소드에 인스턴스 메소드 사용 가능(O)
field2 = 20; //인스턴스 멤버 클래스 메소드에 정적 필드 사용 가능(O)
method2(); //인스턴스 멤버 클래스 메소드에 정적 메소드 사용 가능(O)
}
}
static class C { //정적 멤버 클래스
void method() {
field1 = 10; //정적 멤버 클래스 메소드에 인스턴스 필드 사용 불가능(X)
method1(); //정적 멤버 클래스 메소드에 인스턴스 메소드 사용 불가능(X)
field2 = 20; //정적 멤버 클래스 메소드에 정적 필드 사용 가능(O)
method2(); //정적 멤버 클래스 메소드에 정적 메소드 사용 가능(O)
}
}
}
로컬 클래스에서 사용제한
메소드 안에서 선언된 로컬 클래스 내부에서 바깥의 멤버(필드 및 메소드)는 사용이 가능하다. 하지만 나중에 포스팅할 멀티 쓰레드라는 개념이 나올시에는 상황이 달라진다. 쉽게 말해 백그라운드에서 비동기식으로 움직이는 형태인데 나중에 자세히 포스팅할 예정이다.
여기서 가만히 생각해보면 메소드의 실행이 끝나게 되면 매개 변수 및 로컬 변수를 Garbage collector는 필요 없는 객체라고 판단하여 스택 메모리에서 존재를 지운다. 로컬 클래스의 객체는 메소드 실행이 끝나도 힙 메모리에 존재해 계속 사용할 수 있는데 그렇게 된 상태에서 로컬 객체를 사용하게 되면 문제가 발생하게 된다.
이것을 해결하기 위해 자바는 컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해두고 사용한다. 만일 값이 변경된다면 로컬 클래스 내부에 복사해둔 값과 달라지게 되니 final로 선언하야한다.
java 8 이후에는 final을 선언했다면 로컬 클래스의 메소드 내부에 지역 변수로 복사되며 그렇지 않은 경우 로컬 클래스의 필드로 복사된다.
public class A {
public void method(int arg) {
int localVariable = 1;
//arg = 100; final속성을 가지고 있기 때문에 변경 X
//localVariable = 100; final속성을 가지고 있기 때문에 변경 X
class Inner {
public void mehtod() {
int result = arg + localVariable;
}
}
}
}
중첩클래스에서 바깥 클래스 참조 얻기
중첩 클래스 안에서 바깥 클래스의 참조를 얻기 위해서는 바깥.this.필드; 를 사용해야 한다. 그냥 this를 사용하게 되면 객체 자신을 참조하기 때문에 가장 가까운 클래스를 참조하게 된다.
'JAVA' 카테고리의 다른 글
14. Object 클래스(hashcode() 및 equals()) (0) | 2023.11.15 |
---|---|
13. 익명 객체 (0) | 2023.11.11 |
11. 추상 클래스와 인터페이스의 차이 (0) | 2023.11.09 |
10. 인터페이스 (0) | 2023.11.09 |
9. 추상 클래스 (0) | 2023.11.09 |