java/Class.Interface.Method

[Java] 내부 클래스

잘할수있을거야 2021. 8. 29. 14:46

코틀린 내부 클래스

https://hellose7.tistory.com/119

 

[kotlin] Nested Class

[Java/Nested Class] - [Java] 내부 클래스 [Java] 내부 클래스 non-static 내부 클래스 인스턴스 생성법 반드시 외부 클래스 인스턴스를 통해 인스턴스를 생성해야한다. class Outer { class Inner { } } public..

hellose7.tistory.com

 

내부 클래스란 클래스 안에 선언된 클래스를 의미한다.

 

http://www.tcpschool.com/java/java_usingClass_innerClass

 

보통 non-static 내부 클래스를 Inner 클래스라고 부르고, static 내부 클래스를 static nested 클래스라고 부른다.


non-static  내부 클래스

 

  • 인스턴스 생성법

 

반드시 외부 클래스 인스턴스를 통해 인스턴스를 생성해야한다.

class Outer {
	class Inner {
	}
}

public class NestedClassTest {

	public static void main(String[] args) {
		Outer outer = new Outer();
		System.out.println(outer.toString());
		
		Outer.Inner inner = outer.new Inner(); 
		System.out.println(inner.toString());
	}
}


  • 외부클래스명.this

 

non static 내부 클래스의 인스턴스가 생성될시에는 외부 클래스의 인스턴스가 항상 존재한다. 

그렇기 때문에 내부 클래스의 인스턴스를 사용할 수 있는 곳(생성자,멤버변수메서드)에서는 외부 클래스의 인스턴스를 나타내는 외부클래스.this 를 사용할 수 있다.

class Outer {
	class Inner {
		public void printOuter() {
			System.out.println(Outer.this);
		}

		public void printThis() {
			System.out.println(this);
			// = System.out.println(Inner.this);
		}
	}
}

public class NestedClassTest {

	public static void main(String[] args) {
		Outer outer = new Outer();
		System.out.println(outer.toString());

		Outer.Inner inner = outer.new Inner();
		inner.printOuter();
		
	}
}

 

 

this 대신 인스턴스 멤버를 직접 참조하는 것도 가능하다.

class Outer {

	private String mString = "외부 인스턴스 멤버";

	private void mMethod() {
		System.out.println("외부 인스턴스 메서드");
	}

	class Inner {

		String str = mString;

		void method() {
			mMethod();
		}

	}
}

  • 메모리 누수

 

https://d2.naver.com/helloworld/329631

 

class Outer {

	class Inner {
		
		public void printOuter() {
			System.out.println(Outer.this);
		}
	}
	
}

public class NestedClassTest {

	public static void main(String[] args) {
		Outer outer = new Outer();
		System.out.println(outer.toString());

		Outer.Inner inner = outer.new Inner();
		outer = null; //외부 인스턴스 null
		
		inner.printOuter();
		

	}
}

 

non static 내부 클래스는 자기를 포함하는 인스턴스의 참조를 가지고 있다.

outer = null; 로 초기화하여도 루트부터 inner -> Inner -> Outer가 참조가 가능하므로 가비지 컬렉션의 대상이 되지 않는다. 

inner =null; 로 해주지 않으면 main함수의 종료까지 쓸데없이 메모리를 사용하게 된채로 어플리케이션이 종료될 때까지 지 메모리에 버티고 있는 것이다.


  • 사용가능한 변수, 메서드

 

1. 인스턴스 멤버(변수,메서드)는 클래스 멤버,인스턴스 멤버 모두 사용가능

2. 클래스 멤버는 클래스 멤버만 사용가능 (인스턴스 멤버 사용불가) . 인스턴스 멤버를 사용하려면 인스턴스를 생성한 뒤 사용해야함

 

class Outer {

	private static String sString = "클래스 멤버";
	private String mString = "인스턴스 멤버";

	private static String sMethod() {
		return "클래스 메서드";
	}

	private String mMethod() {
		return "인스턴스 메서드";
	}

	class Inner {
		
		//인스턴스 멤버는 외부의 모든 것 사용가능
		private String str1 = sString;
		private String str2 = sMethod();
		private String str3 = mString;
		private String str4 = mMethod();

		//클래스 멤버는 외부의 클래스 멤버만 사용가능
		private static String str5 = sString;
		private static String str6 = sMethod();
		private static String str7 = mString; // 에러
		private static String str8 = mMethod(); // 에러

		//인스턴스 멤버는 외부의 모든 것 사용가능
		private void innerMethod() {
			String str1 = sString;
			String str2 = mString;
			String str3 = sMethod();
			String str4 = mMethod();

		}

		//클래스 멤버는 외부의 클래스 멤버만 사용가능
		private static void innerStaticMethod() {
			String str1 = sString;
			String str2 = sMethod();
			String str3 = mString; // 에러
			String str4 = mMethod(); // 에러
		}
	}

}

 

  • 외부 클래스와 내부 클래스의 메모리 로딩

 

static{} 클래스 초기화 블럭:  클래스가 메모리에 처음 로딩될 때만 단 한번 호출된다.

 

외부,내부 클래스 정의 상태

class Outer {
	
	static String sOuterString;
	
	static {
		System.out.println("외부 클래스 메모리 로딩");
	}

	class Inner {
		
		static String sInnerString;
		
		static {
			System.out.println("내부 클래스 메모리 로딩");
		}
	}

}

 

main함수

 

클래스를 메모리에 로딩시키기 위해 클래스 멤버(변수,메서드)를 호출한다.

public class NestedClassTest {

	public static void main(String[] args) {
		
		Outer.sOuterString="클래스 멤버에 값을 대입하여 클래스 메모리 로딩시킴";

	}
}

 

public class NestedClassTest {

	public static void main(String[] args) {

		Outer.Inner.sInnerString = "클래스 멤버에 값을 대입하여 클래스 메모리 로딩시킴";

	}
}

 

내부 클래스의 클래스 멤버가 호출될때 내부 클래스만 메모리에 로딩된다.

외부 클래스가 같이 로딩되지 않았다.

쉬운 이해를 위해 다음의 코드를 추가적으로 보자.

 

class Outer {

	static String sOuterString = "외부의 클래스 멤버";

	static {
		System.out.println("외부 클래스 메모리 로딩");
	}

	class Inner {

		static String sInnerString;

		static {
			System.out.println("내부 클래스 메모리 로딩");
			System.out.println("sInnerString: " + sInnerString);
		}

		static void sInnerMethod() {
			System.out.println("내부의 클래스 메서드 호출");

			sInnerString = sOuterString;
			//main함수의 코드에 Inner부터 클래스 로딩시키므로
            		//Outer는 sOuterString을 사용되는 여기서 로딩됨

			System.out.println("sInnerString: " + sInnerString);

			System.out.println("내부의 클래스 메서드 종료");
		}
	}

}

public class NestedClassTest {

	public static void main(String[] args) {

		Outer.Inner.sInnerMethod();

	}
}

 

main함수의 코드만 변경시킨 것 (외부 클래스를 먼저 로딩시킴)

class Outer {

	static String sOuterString;

	static {
		System.out.println("외부 클래스 메모리 로딩");
	}

	class Inner {

		static String sInnerString;

		static {
			System.out.println("내부 클래스 메모리 로딩");
			System.out.println("sInnerString: " + sInnerString);
		}

		static void sInnerMethod() {
			System.out.println("내부의 클래스 메서드 호출");

			sInnerString = sOuterString;

			System.out.println("sInnerString: " + sInnerString);

			System.out.println("내부의 클래스 메서드 종료");
		}
	}

}

public class NestedClassTest {

	public static void main(String[] args) {

		Outer.sOuterString = "외부의 클래스 멤버"; //외부 클래스 먼저 로딩
		Outer.Inner.sInnerMethod();

	}
}

이렇게 클래스 멤버의 동작을 알아봤다.

 

 

그러나 내부 클래스는 외부 클래스의 도움 목적이므로, 외부 클래스의 클래스 멤버는 내부 클래스의 클래스 멤버를 참조할 수 없게 만들었다.


static 내부 클래스

 

  • 인스턴스 생성법

 

static 내부 클래스의 인스턴스는 외부 클래스의 인스턴스와 아무런 관계가 없다.

즉 독자적으로 생성이 가능하다. 그러나 외부 클래스에 포함된 클래스이므로 new 외부클래스명.클래스();로 사용

class Outer {
	static class StaticInner {}
}

public class NestedClassTest {

	public static void main(String[] args) {

		Outer.StaticInner staticInner = new Outer.StaticInner();
		System.out.println(staticInner);
	}
}


  • this 키워드

 

외부 클래스의 인스턴스와 관계가 존재하지 않으므로 외부클래스.this같은 행위는 가능하지 않다.


  • 외부 클래스의 클래스 멤버만 사용가능하다.

 

class Outer {
	
	static String sString = "외부의 클래스 멤버";

	static void sMethod() {
		System.out.println("외부의 클래스 메서드");
	}

	static class StaticInner {

		static String innerStaticString= sString;
		static void innerStaticMethod() {
			sMethod();
		}
	}

}

  • 클래스 메모리 로딩

 

위에서 살펴본 non-static처럼 외부 내부는 독자적으로 클래스 로딩이 이루어진다.


  • 익명 클래스

일회성으로 인스턴스를 전달하기 위해 사용된다.

 

[Java/Class] - [Java] 익명 클래스

 

[Java] 익명 클래스

-익명 클래스란 class A 를 상속받아 어떠한 기능을 하는 class B를 만들어야 한다고 가정하자. class B extends A로 클래스 B를 선언한 뒤에 new B를 통해 객체를 생성한다. 만약 프로그램에서 클래스 B는

hellose7.tistory.com

익명 클래스 사용시 메모리 릭이 발생할 수 있는 상황이 존재한다.


(내용 추가)

kotlin object 선언식 공부중 막힌 자바 여러 중첩 레벨 클래스

 

다음의 클래스 선언을 보자.

Nested 클래스의 인스턴스를 생성할 때 Outer, Inner 인스턴스 모두 필요없다.

(static클래스의 인스턴스는 모든 외부 클래스들의 인스턴스에 관계없이 독자적으로 생성될 수 있다)

public class TwoLevelStaticClassTest {

	public static void main(String[] args) {
		
		Outer.Inner.Nested nested= new Outer.Inner.Nested(); 
        	//모든 외부 클래스의 인스턴스 필요없다.
		nested.method();
	}
}