관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch7 - 42 ~ 52[내부클래스의종류,특징,선언, 내부클래스의 제어자와 접근성, 익명클래스] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch7 - 42 ~ 52[내부클래스의종류,특징,선언, 내부클래스의 제어자와 접근성, 익명클래스]

소소한나구리 2023. 11. 30. 12:45

1) 내부클래스

  • 클래스 안의 클래스

(1) 장점

  • 내부 클래스에서 외부 클래스의 멤버들을 외부클래스의 객체를 만들지 않고 접근이 가능함
  • 코드의 복잡성을 줄일 수 있음 - 일종의 캡슐화
  • B클래스를 굳이 밖에서 사용할 일이 없을 때 내부클래스로 생성 -> 별도의 방법으로 사용은 가능함
class A { // B의 외부클래스
	....
    class B { // A의 내부클래스 - 객체생성없이도 A의 멤버 접근 가능
    ...
    }
    ...
}

 

(2) 내부 클래스의 종류별 유효범위

  • iv = 내부클래스
  • cv = static 내부 클래스
  • lv = 메서드안에 선언된 내부 클래스
class outer {
	int iv = 0;	        // iv = 내부클래스와 동일
	static int cv = 0;	// cv = static 내부클래스와 동일
    
	void myMethod() {	
		int lv = 0;	// lv = 메서드 안에 선언된클래스(지역클래스)와 동일
    }
}


class outer {
	class InstanceInner     { }	// 내부클래스 = iv와 동일
	staic class StaticInner { }	// static내부 클래스(스태틱클래스 = cv와 동일
    
	void myMethod() {
		class localInner { }	// 메서드 안에 선언된클래스(지역클래스) = lv
    }
}

 

(3) 내부 클래스의 제어자와 접근성

  • 내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일하게 전부 사용 가능
  • 내부 클래스에 static멤버를 담을 수 있는 것은 static 내부클래스만 허용 - 핵심(상수는 예외)
class ex7_12 { 
	class InstanceInner { 
		int iv = 100; 
//		static int cv = 100;            // 에러! static변수를 선언할 수 없다. 
		final static int CONST = 100;   // final static은 상수이므로 허용
	} 

   static class StaticInner { 
		int iv = 200; // static 내부 클래스의 iv
		static int cv = 200;    // static내부클래스만 static멤버를 정의할 수 있다. 
	} 

	void myMethod() { 
		class LocalInner { 
			int iv = 300; 
//			static int cv = 300;             // 에러! static변수를 선언할 수 없다. 
			final static int CONST = 300;    // final static은 상수이므로 허용 
		}
	} 

	public static void main(String args[]) { 
		System.out.println(InstanceInner.CONST); 
		System.out.println(StaticInner.cv); 
//		System.out.println(LocalInner.CONST);	// 에러(지역내부클래스는 메서드 밖에서 사용 불가) 
	} 
}

 

** 참고

  • 상수(final)인 경우에는 static을 붙히는 경우가 많지만 경우에따라 조금 제어자의 적용 여부가 달라질 수 있음
  • 값이 변하지 않지만 공통적으로 사용하지 않는 값 - static 안붙힘
  • 값이 변하지 않지만 공통적으로 사용하는 값 - static 붙힘
  • 상수는 constant pool이 있어서 별도의 공간에 저장되어 메서드가 종료 되어도 상수는 남아있으므로 계속 사용 가능함

(4) 예제1

  • 내부 클래스들의 종류별 객체 생성
  • 내부 클래스들의 유효범위가 iv, cv, lv와 같기 때문에 이들의 특성이 그대로 적용된다고 보면됨
  • 내부클래스는 객체를 생성해야하고, 스태틱 클래스는 바로 접근이 가능함
  • 스태틱 메서드에서는 내부 클래스는 바로 접근이가능하지만 외부클래스는 객체를 생성해야만 접근이가능한 반면 인스턴스 메서드에서는 둘다 직접 접근이 가능함
class Ex7_13 {
	class InstanceInner {}			// 내부클래스
	static class StaticInner {}		// 스태틱 내부클래스

	// 인스턴스멤버 간에는 서로 직접 접근이 가능하다.
	InstanceInner iv = new InstanceInner();
	// static 멤버 간에는 서로 직접 접근이 가능하다.
	static StaticInner cv = new StaticInner();

    // static멤버는 인스턴스멤버에 직접 접근할 수 없음. cv는 iv,im 사용 불가한 것과 같은 이치
	static void staticMethod() {
//		InstanceInner obj1 = new InstanceInner();	// static메서드에서 생성 불가
		StaticInner obj2 = new StaticInner();

		// 굳이 접근하려면 아래와 같이 객체를 생성해야 함
		// 내부클래스는 외부 클래스를 먼저 생성해야만 생성할 수 있음
		Ex7_13 outer = new Ex7_13();
		InstanceInner obj1 = outer.new InstanceInner();
	}

  	// 인스턴스메서드에서는 인스턴스멤버와 static멤버 모두 접근 가능gka
	void instanceMethod() {
		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
        
		// 메서드 내에 지역적으로 선언된 내부 클래스는 외부에서 접근할 수 없음
//		LocalInner lv = new LocalInner();
	}

	void myMethod() {
		class LocalInner {}		// 지역 내부 틀래스
		LocalInner lv = new LocalInner();
	}
}

 

(5) 예제2

  • 외부 클래스에 private 제어자가 붙어있어도 내부클래스에서는 접근 가능함
  • 지역내부클래스(메서드 안의 클래스)에서 외부 클래스의 지역변수는(lv) 상수일때만 사용가능 했지만 JDK1.8(Java8) 부터는 변수도 사용 가능함
  • 변수인데 값이 프로그램 실행 동안에 값이 바뀌지 않는 것은 상수로 간주함
class ex7_14 {
	private int outerIv = 0;
	private 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 생략 가능

//		lv = 3 이런식으로 중간에 값을 바꾸게 되면 에러
		
		class LocalInner {
			int liv  = outerIv;
			int liv2 = outerCv;
            
			//	외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능했지만 바뀜
			int liv3 = lv;	// JDK1.8(Java8)부터 에러가 아니게 바뀌었음
			int liv4 = LV;	// OK
		}
	}
}

 

(6) 예제3

  • 내부클래스는 객체를 생성하여 참조된 참조변수로 접근을 해야하고, 내부 객체를 생성하기 위해선 내부 클래스를 정의한 외부 클래스의 객체를 생성해야함
  • 스태틱 내부 클래스는 클래프명으로 직접 접근하면 됨
  • 컴파일하면 class파일이 5개 만들어짐
  • 내부클래스는 외부클래스명+$+내부클래스명 으로 생성됨
  • 지역내부클래스는 외부클래스명+$+숫자+내부클래스명으로 생성됨
class Outer2 {
	class InstanceInner {
		int iv = 100;
	}

	static class StaticInner {
		int iv = 200;
		static int cv = 300;
	}

	void myMethod() {
		class LocalInner {
			int iv = 400;
		}
	}
}

class ex7_15 {
	public static void main(String[] args) {
		// 인스턴스클래스의 인스턴스를 생성하려면 외부 클래스의 인스턴스를 먼저 생성
		Outer2 oc = new Outer2();	// 외부클래스 인스턴스 생성
		Outer2.InstanceInner ii = oc.new InstanceInner();	// 내부클래스의 인스턴스 생성

		System.out.println("ii.iv : "+ ii.iv);	// iv출력
		
		// 외부클래스명.내부클래스명.cv명
		System.out.println("Outer2.StaticInner.cv : "+Outer2.StaticInner.cv);	 //cv출력
		                                     
		// 스태틱 내부 클래스의 iv는 내부클래스 객체를 바로 생성(외부클래스 객체 생성안해도 됨)
		// 외부클래스.내부클래스명 변수명 = new 외부클래스명.내부클래스명();
		Outer2.StaticInner si = new Outer2.StaticInner();
		System.out.println("si.iv : "+ si.iv);
	}
}

// 컴파일하면 class파일이 5개 만들어짐
// 내부클래스에는 외부클래스명+$+내부클래스명
// 지역내부클래스에는 외부클래스명+$+숫자+내부클래스명으로 생성

 

(7) 예제4

  • 변수의 이름이 같을 때 구별 방법
  • 외부클래스 변수 - 외부클래스명.this.iv명
  • 내부클래스 변수 - this.iv명
  • 지역변수 - lv명
class Outer3 {
	int value = 10;	// Outer3.this.value (외부클래스의 iv)

	class Inner {
		int value = 20;   // this.value (내부클래스의 iv)

		void method1() {
			int value = 30;
			System.out.println("            value :" + value);
			System.out.println("       this.value :" + this.value);
			System.out.println("Outer3.this.value :" + Outer3.this.value);
		}
	} // Inner클래스의 끝
} // Outer3클래스의 끝

class ex7_16 {
	public static void main(String args[]) {
		Outer3 outer = new Outer3();
		Outer3.Inner inner = outer.new Inner();
		inner.method1();
	}
}

2) 익명클래스(anonymous class)

  • 이름이 없는 일회용 클래스 -> 정의와 생성을 동시에함
  • 익명클래스도 내부클래스의 일종이라고 볼 수 있음
  • 자신의 이름이 없어서 조상의 이름으로 객체를 구현함
  • 컴파일하면 클래스명이 없으므로 숫자로 표현됨 -> 외부클래스명+$+숫자
  • 익명클래스가 정의되고 해당 전체 문장이 끝날때 세미콜론을 붙혀야함, 즉 익명 클래스를 정의할때 ;을 붙히는게 아니라 익명클래스를 정의하고 문장이 마무리 되었기 때문에 ;을 붙히는 것임

(1) 예제1

// 기존의 클래스 정의 및 객체 생성 방법
class MyClass extends Object { /* 멤버선언 등 */ }   // 클래스정의
MyClass mc = new MyClass();                       // 객체생성

// 익명 클래스를 생성
new 조상클래스이름() {
	// 멤버선언
};	// 문장이 종료 되었으므로 세미콜론을 붙혀야함

// 	또는
new 구현인터페이스이름() {
	// 멤버선언
};	// 문장이 종료 되었으므로 세미콜론을 붙혀야함

 

(2) 예제2

  • 다양한 익명 클래스 정의 방법
class Ex7_17 {
	Object iv = new Object(){ // 익명 클래스를 생성하여 iv에 참조값을 저장
		void method(){}
	};
    
	// 익명 클래스를 생성하여 static 변수인 cv에 참조값을 저장
	static Object cv = new Object(){ 
		void method(){}
	};  

	void myMethod() {
    	// 메서드 내부에 익명 클래스를 정의하여 lv에 참조값을 저장
		Object lv = new Object(){
			void method(){}
		};      
	}
}

 

(3) 예제3

  • 일반 클래스 -> 익명클래스를 전환
  • 클래스를 일회성으로 사용해야할 때 사용됨
  • 즉 재사용할 필요가 없이 단발성인 경우에 클래스를 선언하지 않고 즉시 사용할 때 사용한다고 보면 됨
import java.awt.*;
import java.awt.event.*;

class Ex7_18 {
	public static void main(String[] args) {
		Button b = new Button("Start");
		b.addActionListener(new EventHandler());
	}
}
class EventHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		System.out.println("ActionEvent occurred!!!");
	}
}


//익명클래스로 전환 시
class Ex7_18 {
	public static void main(String[] args) {
		Button b = new Button("Start");
		b.addActionListener(new ActionListener() {	// 클래스의정의와 객체생성을 동시에
			public void actionPerformed(ActionEvent e) {
				System.out.println("ActionEvent occurred!!!");
			}
		});
	}
}

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의