인스턴스클래스와 스태틱 클래스는 외부 클래스와 멤버변수와 같은 위치에 선언되며, 멤버변수와 같은 성질을 갖는다.
내부 클래스도 클래스이기 때문에 abstract나 final과 같은 제어자를 사용할 수 있을 뿐만 아니라, 멤버변수들처럼 private,protected과 접근제어자도 사용이 가능하다.
내부 클래스 중에서 스태틱 클래스만 static 멤버를 가질 수 있다.
final static이 동시에 붙은 변수는 상수이므로 모든 내부 클래스에서 사용가능하다.
class InnerEx1 {
class InstanceInnter {
int iv = 100;
//static int cv = 100; => 에러
final static int CONST = 100; // => 허용
}
static class StaticInner {
int iv = 200;
static int cv = 200;
}
void myMethod() {
class LocalInner {
int iv = 300;
//static int cv = 300; => 에러
final static int CONST = 300; // => 허용
}
}
public static void main(String[] args) {
System.out.println(InstanceInner.CONST); //100
System.out.println(StaticInner.cv); // 200
}
}
인스턴스 클래스는 외부 클래스의 인스턴스 멤버를 객체생성 없이 바로 사용할 수 있지만, 스태틱 클래스는 외부 클래스의 인스턴스 멤버를 객체생성 없이 사용할 수 없다. 인스턴스 클래스는 스태틱 클래스의 멤버들을 객체생성 없이 사용할 수 있지만, 스태틱 클래스에서는 인스턴스의 멤버들을 객체생성 없이 사용할 수 없다.
package com.company;
class InnerEx2 {
class InstanceInner {}
static class StaticInner {}
// 인스턴스멤버 간에는 서로 직접 접근이 가능하다.
InstanceInner iv = new InstanceInner();
// static 멤버 간에는 서로 직접 접근이 가능하다.
static StaticInner cv = new StaticInner();
static void staticMethod() {
// static멤버는 인스턴스멤버에 직접 접근할 수 없다.
// InstanceInner obj1 = new InstanceInner();
StaticInner obj2 = new StaticInner();
// 굳이 접근하려면 아래와 같이 객체를 생성해야 한다.
// 인스턴스클래스는 외부 클래스를 먼저 생성해야만 생성할 수 있다.
InnerEx2 outer = new InnerEx2();
InstanceInner obj1 = outer.new InstanceInner();
}
void instanceMethod() {
// 인스턴스메서드에서는 인스턴스멤버와 static멤버 모두 접근 가능하다.
InstanceInner obj1 = new InstanceInner();
StaticInner obj2 = new StaticInner();
// 메서드 내에 지역적으로 선언된 내부 클래스는 외부에서 접근할 수 없다.
// LocalInner lv = new LocalInner();
}
void myMethod() {
class LocalInner {}
LocalInner lv = new LocalInner();
}
}
인스턴스 클래스는 외부 클래스의 인스턴스 멤버이기 때문에 인스턴스 멤버와 static 멤버를 모두 사용할 수 있다.
스태틱 클래스는 외부 클래스의 static 멤버이기 때문에 인스턴스 멤버를 사용할 수 없다. 단지 static 멤버만을 사용할 수 있다.
지역 클래스는 외부 클래스의 인스턴스 멤버와 static 멤버를 모두 사용할 수 있으며, 지역 클래스가 포함된 메서드에 정의된 지역변수도 사용할 수 있다. 단, final이 붙은 지역변수만 접근 가능하다.
package com.company;
class InnerEx3 {
private int outerIv = 0;
static int outerCv = 0;
class InstanceInner {
int iiv = outerIv; // 외부 클래스의 private멤버도 접근가능하다.
int iiv2 = outerCv;
}
static class StaticInner {
// 스태틱 클래스는 외부 클래스의 인스턴스멤버에 접근할 수 없다.
// int siv = outerIv;
static int scv = outerCv;
}
void myMethod() {
int lv = 0;
final int LV = 0; // JDK1.8부터 final 생략 가능
class LocalInner {
int liv = outerIv;
int liv2 = outerCv;
// 외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능하다.
int liv3 = lv; // 에러!!!(JDK1.8부터 에러 아님)
int liv4 = LV; // OK
}
}
void method() {
}
}
메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도, 지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문이다.
외부 클래스가 아닌 다른 클래스에서 내부 클래스를 생성하고 내부 클래스의 멤버에 접근하는 예제이다.
package com.company;
class Outer1 {
class InstanceInner {
int iv=100;
}
static class StaticInner {
int iv=200;
static int cv=300;
}
void myMethod() {
class LocalInner {
int iv=400;
}
}
}
class InnerEx4 {
public static void main(String[] args) {
// 인스턴스클래스의 인스턴스를 생성하려면
// 외부 클래스의 인스턴스를 먼저 생성해야 한다.
Outer1 oc = new Outer1();
Outer1.InstanceInner ii = oc.new InstanceInner();
System.out.println("ii.iv : "+ ii.iv);
System.out.println("Outer.StaticInner.cv : " + Outer1.StaticInner.cv);
// 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
Outer1.StaticInner si = new Outer1.StaticInner();
System.out.println("si.iv : "+ si.iv);
}
}
내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this' 키워드와 '외부 클래스명.this'를 붙여서 구별할 수 있다.
package com.company;
class Outer2 {
int value=10; // Outer.this.value
class Inner {
int value=20; // this.value
void method1() {
int value=30;
System.out.println(" value :" + value);
System.out.println(" this.value :" + this.value);
System.out.println("Outer.this.value :" + Outer2.this.value);
}
} // Inner클래스의 끝
} // Outer클래스의 끝
class InnerEx5 {
public static void main(String args[]) {
Outer2 outer = new Outer2();
Outer2.Inner inner = outer.new Inner();
inner.method1();
}
} // InnerEx5 끝