멤버 클래스
: 내부 클래스
+ 정적 내부 클래스
지역 내부 클래스
class Outer { // 외부 클래스
class Inner {
// 인스턴스 내부 클래스
}
static class StaticInner {
// 정적 내부 클래스
}
void run() {
class LocalInner {
// 지역 내부 클래스
}
}
}
종 류 | 선언 위치 | 사용 가능한 변수 (static의 유무) |
---|---|---|
인스턴스 내부 클래스 (instance inner class) | 외부 클래스의 멤버변수 선언위치에 선언 (멤버 내부 클래스) | 외부 인스턴스 변수, 외부 전역 변수 (외부 클래스에 모두 접근 가능) |
정적 내부 클래스 (static inner class) | 외부 클래스의 멤버변수 선언위치에 선언 (멤버 내부 클래스) | 외부 전역 변수 |
지역 내부 클래스 (local inner class) | 외부 클래스의 메서드나 초기화블럭 안에 선언 | 외부 인스턴스 변수, 외부 전역 변수 |
익명 내부 클래스 (anonymous inner class) | 클래스의 선언과 객체의 생성을 동시에 하는 일회용 익명 클래스 | 외부 인스턴스 변수, 외부 전역 변수 |
static
이 붙은 정적 내부 클래스
의 경우에는 static 변수/메서드
만 가능하다.class Outer { //외부 클래스
private int num = 1; //외부 클래스 인스턴스 변수
private static int sNum = 2; // 외부 클래스 정적 변수
private InClass inClass; // 내부 클래스 자료형 변수 선언
public Outer() { //외부 클래스 생성자
inClass = new InClass(); // 내부 클래스 인스턴스를 생성
}
class InClass { //인스턴스 내부 클래스
int inNum = 10; //내부 클래스의 인스턴스 변수
void Test() {
System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
}
}
public void testClass() {
inClass.Test();
// 외부 클래스에서 내부 클래스 인스턴스를 사용하지 않고 접근 불가!
// Test();
// inNum = 5;
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
System.out.println("외부 클래스 사용하여 내부 클래스 기능 호출");
outer.testClass(); // 내부 클래스 기능 호출
}
}
// 출력값
외부 클래스 사용하여 내부 클래스 기능 호출
Outer num = 1(외부 클래스의 인스턴스 변수)
Outer sNum = 2(외부 클래스의 정적 변수)
✅ 위의 코드를 통해서, 외부 클래스에서 내부 클래스에 접근하는 방법을 알 수 있다.
1) 외부 클래스에서 내부 클래스의 인스턴스를 생성
2) 내부 클래스 인스턴스에 포인트 연산자를 사용하여, 내부 클래스의 멤버 변수에 접근할 수 있다.
✅ 외부 클래스에서 내부 클래스 인스턴스를 사용하지 않고, 직접 내부 클래스의 멤버에는 접근할 수 없다!
외부_클래스_이름.내부_클래스_이름 = new 외부_클래스_이름.내부_클래스_이름()
class Outer { //외부 클래스
private int num = 3; //내부 클래스의 인스턴스 변수
private static int sNum = 4;
void getPrint() {
System.out.println("인스턴스 메서드");
}
static void getPrintStatic() {
System.out.println("스태틱 메서드");
}
static class StaticInClass { // 정적 내부 클래스
void test() {
System.out.println("Outer num = " +sNum + "(외부 클래스의 정적 변수)");
getPrintStatic();
// num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
}
}
}
public class Main {
public static void main(String[] args) {
Outer.StaticInClass a = new Outer.StaticInClass(); //정적 이너 클래스의 객체 생성
a.test();
}
}
//출력값
Outer num = 4(외부 클래스의 정적 변수)
스태틱 메서드
class Outer { //외부 클래스
private int num = 3; //내부 클래스의 인스턴스 변수
private static int sNum = 4;
void getPrint() {
System.out.println("인스턴스 메서드");
}
static void getPrintStatic() {
System.out.println("스태틱 메서드");
}
class inClass { // 인스턴스 클래스
void test() {
System.out.println("Outer num = " +num + "(외부 클래스의 인스턴스 변수)");
getPrint();
}
}
static class StaticInClass { // 정적 내부 클래스
void test() {
System.out.println("Outer num = " +sNum + "(외부 클래스의 정적 변수)");
getPrintStatic();
// num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
}
}
}
public class Main {
public static void main(String[] args) {
//정적 이너 클래스의 객체 생성
Outer.StaticInClass a = new Outer.StaticInClass();
a.test();
// 인스턴스 클래스의 객체 생성
Outer o = new Outer();
Outer.inClass b = o.new inClass();
// 다른 방법
Outer.inClass b = new Outer().new inClass();
b.test();
}
}
// 출력
Outer num = 4(외부 클래스의 정적 변수)
스태틱 메서드
Outer num = 3(외부 클래스의 인스턴스 변수)
인스턴스 메서드
⭐ 정적 내부 클래스:
Outer.StaticInClass a = new Outer.StaticInClass();
⭐ 인스턴스 내부 클래스:Outer.inClass b = new Outer().new inClass();
✅ 외부 클래스의 인스턴스를 생성한 후에, 외부 클래스 인스턴스 안에 내부 클래스 인스턴스가 만들어지므로, 인스턴스 내부 클래스는 new 연산자를 2번 사용해야 한다.
class Outer { //외부 클래스
int num = 5;
void test() {
int num2 = 6;
class LocalInClass { //지역 내부 클래스
void getPrint() {
System.out.println(num);
System.out.println(num2);
}
}
LocalInClass localInClass = new LocalInClass();
localInClass.getPrint();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
//출력값
5
6
https://johngrib.github.io/wiki/java-inner-class-may-be-static/
내부 클래스가 외부 클래스의 멤버를 참조하지 않는다면, 내부 클래스를 static으로 선언하자.
다음과 같은 3가지 이유가 있다.
static이 아닌 경우, 외부 클래스 인스턴스를 통해서만 객체를 생성할 수 있기 때문에 독립적으로 존재할 수가 없다.
static이 아닌 내부 클래스의 인스턴스는 외부 클래스의 인스턴스와 암묵적으로 연결된다.
이때 내부 클래스의 인스턴스 안에 외부 클래스로의 외부 참조를 갖게 된다.
그 외부 참조는 메모리를 차지하며 생성도 느리다.(시간과 공간이 낭비)
외부 클래스 인스턴스 참조를 내부 클래스가 가지고 있기 때문에, 가비지 컬렉션이 외부 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수도 있다.
(외부 클래스를 더 이상 사용하지 않지만, 내부 클래스의 참조로 인해서 GC가 수거하지 못할 수도 있기 때문에)