-
[Java] 내부 클래스java/Class.Interface.Method 2021. 8. 29. 14:46
코틀린 내부 클래스
https://hellose7.tistory.com/119
내부 클래스란 클래스 안에 선언된 클래스를 의미한다.
보통 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처럼 외부 내부는 독자적으로 클래스 로딩이 이루어진다.
- 익명 클래스
일회성으로 인스턴스를 전달하기 위해 사용된다.
익명 클래스 사용시 메모리 릭이 발생할 수 있는 상황이 존재한다.
(내용 추가)
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(); } }
'java > Class.Interface.Method' 카테고리의 다른 글
[Java] 클래스 상속관계에서의 static 메서드 재정의 (0) 2022.02.06 [Java] Interface 나중 참고용 (0) 2022.02.05 [Java] Wrapper class 관련 (0) 2021.12.16 [Java] 익명 클래스 (0) 2021.08.28