diff --git a/images/ch16/GC_behavior_diagram.png b/images/ch16/GC_behavior_diagram.png new file mode 100644 index 0000000..051812a Binary files /dev/null and b/images/ch16/GC_behavior_diagram.png differ diff --git a/images/ch16/nested_class_diagram.png b/images/ch16/nested_class_diagram.png new file mode 100644 index 0000000..0c6423a Binary files /dev/null and b/images/ch16/nested_class_diagram.png differ diff --git a/scripts/ch_16/README.md b/scripts/ch_16/README.md new file mode 100644 index 0000000..f0dd602 --- /dev/null +++ b/scripts/ch_16/README.md @@ -0,0 +1,19 @@ + +### Chapter 16 : 클래스 안에 클래스가 들어갈 수도 있구나 + +- [`1. 클래스 안의 클래스`](./section_01_04.md#1-클래스-안의-클래스) +- [`2. static nested 클래스의 특징`](./section_01_04.md#2-static-nested-클래스의-특징) +- [`3. 내부 클래스와 익명 클래스`](./section_01_04.md#3-내부-클래스와-익명-클래스) +- [`4. Nested 클래스의 특징은 꼭 알아야 한다`](./section_01_04.md#4-nested-클래스의-특징은-꼭-알아야-한다) + +--- + +### What I learned from this chapter + +이번 챕터에는 `Nested Class` 에 대해 배웠다. 중첩 클래스는 **잘** 만들면 코드의 가독성, 유지보수성을 높일 수 있는 좋은 전략이다. + +이러한 `Nested Class` 를 어렴풋이 알고 있었지만 이를 형식으로 갖추어 배운적은 처음이다. 그래서 큰 내용을 어렵지 않았지만 세부적으로 몰랐던 내용들이 종종 있었다. + +`Static Nested Class`, `Nested Class 종류에 따른 참조 스코프 범위`, 그리고 `익명 클래스` 등이 그러했다. + +결과적으로 현 챕터에 관한 내용은 그냥 알고있는게 중요한게 아니라 실제로 적용해보면서 체감하는게 중요하다. 어서 프로젝트에서 써먹어 볼 수 있으면 좋겠다. diff --git a/scripts/ch_16/code/InputBox.java b/scripts/ch_16/code/InputBox.java new file mode 100644 index 0000000..d56089e --- /dev/null +++ b/scripts/ch_16/code/InputBox.java @@ -0,0 +1,20 @@ +package scripts.ch_16.code; + +public class InputBox { + public static final int KEY_DOWN = 2; + public static final int KEY_UP = 4; + KeyEventListener listener; + + public void setKeyListener(KeyEventListener listener) { + this.listener = listener; + } + + public void listenerCalled(int eventype) { + if (eventype == KEY_DOWN) { + listener.onKeyDown(); + } + else if (eventype == KEY_UP) { + listener.onKeyUp(); + } + } +} diff --git a/scripts/ch_16/code/KeyEventListener.java b/scripts/ch_16/code/KeyEventListener.java new file mode 100644 index 0000000..865af73 --- /dev/null +++ b/scripts/ch_16/code/KeyEventListener.java @@ -0,0 +1,6 @@ +package scripts.ch_16.code; + +public interface KeyEventListener { + public void onKeyDown(); + public void onKeyUp(); +} diff --git a/scripts/ch_16/code/MyPage.java b/scripts/ch_16/code/MyPage.java new file mode 100644 index 0000000..ca0d986 --- /dev/null +++ b/scripts/ch_16/code/MyPage.java @@ -0,0 +1,39 @@ +package scripts.ch_16.code; + +public class MyPage { + + public static InputBox input; + + static { + input = new InputBox(); + } + + public static void main(String[] args) { + MyPage testMyPage = new MyPage(); + + testMyPage.setUI(); + testMyPage.pressKey(); + } + + public void setUI() { + KeyEventListener listener = new KeyEventListener() { + @Override + public void onKeyDown() { + System.out.println("Key Down"); + } + + @Override + public void onKeyUp() { + System.out.println("Key Up"); + } + }; + + MyPage.input.setKeyListener(listener); + } + + public void pressKey() { + MyPage.input.listenerCalled(InputBox.KEY_DOWN); + MyPage.input.listenerCalled(InputBox.KEY_UP); + } + +} diff --git a/scripts/ch_16/code/README.md b/scripts/ch_16/code/README.md new file mode 100644 index 0000000..7655d96 --- /dev/null +++ b/scripts/ch_16/code/README.md @@ -0,0 +1,57 @@ + +### Chapter 16 : 클래스 안에 클래스가 들어갈 수도 있구나 - 정리해 봅시다. + +--- + +#### 직접 해봅시다 + +여러분들은 자바 기반의 `UI` 를 처리하려고 한다. `InputBox` 라는 클래스는 다음과 같이 구현되어 있다. + +```java +package c.inner.practice; + +public class InputBox { + public InputBox() {} + + KeyEventListener listener; + public void setKeyListener(KeyEventListener listener) { + this.listener = listener; + } + + public static final int KEY_DOWN = 2; + public static final int KEY_UP = 4; + public void listenerCalled(int eventType) { + if (eventType == KEY_DOWN) { + listener.onKeyDown(); + } + else if (eventType == KEY_UP) { + listener.onKeyUp(); + } + } +} +``` + +그리고 `setKeyListener` 에서 사용하는 `KeyEventListener` 인터페이스는 다음과 같이 정의되어 있다. + +```java +package c.inner.practice; + +public interface KeyEventListener { + public void onKeyDown(); + public void onKeyUp(); +} +``` + +- `[1]` : `c.inner.practice` 패키지에 `MyPage` 라는 클래스를 만들고 `main()` 메서드를 추가하자. +- `[2]` : `MyPage` 클래스에 `input` 이라는 이름의 `InputBox` 클래스 변수를 선언하자. +- `[3]` : `MyPage` 클래스에 `setUI()` 라는 리턴값이 없는 메서드를 만들자. +- `[4]` : `setUI()` 메서드에서 `input` 객체를 초기화하자. +- `[5]` : `setUI()` 메서드에 `KeyEventListener` 인터페이스를 구현한 익명 클래스를 만들자. `onKeyDown()` 메서드가 호출되었을 때에는 `"Key Down"` 이, `onKeyUp()` 메서드가 호출되었을 때에는 `"Key Up"` 이 출력되도록 하자. +- `[6]` : `setUI()` 메서드에서 `[5]` 에서 생성한 `listener` 를 `input` 객체의 `setKeyListener()` 메서드에 매개 변수로 넣어주자. +- `[7]` : `MyPage` 클래스에 `pressKey()` 메서드를 추가하자. 이 메서드 내에서는 `InputBox` 클래스에 선언된 `listenerCalled()` 메서드를 호출하여 `onKeyDown()` 이벤트와 `onKeyUp()` 이벤트가 수행되도록 하자. +- `[8]` : `MyPage` 클래스의 `main()` 메서드에서는 `setUI()` 메서드와 `pressKey()` 메서드를 차례로 호출하도록 하자. 이제 컴파일 및 실행을 해보자. 결과는 다음과 같다. + +``` +Kye Down +Key Up +``` diff --git a/scripts/ch_16/code/solution.md b/scripts/ch_16/code/solution.md new file mode 100644 index 0000000..1c3175d --- /dev/null +++ b/scripts/ch_16/code/solution.md @@ -0,0 +1,12 @@ + +### Chapter 16 : 클래스 안에 클래스가 들어갈 수도 있구나 - 정리해 봅시다. + +- [`문제 설명`](./README.md) + +--- + +문제의 설명을 따라 차근차근 진행하면 어렵지 않게 해결할 수 있다. + +다만 문제 `[2]` 에서 `"input 이라는 InputBox 클래스 변수 를 선언"` 하라 하였는데, 이후 문제를 보면 `클래스 변수` 가 아니라 인스턴스 변수로 선언해야 한다. + +나는 그냥 `클래스 변수` 로 만들었다. diff --git a/scripts/ch_16/section_01_04.md b/scripts/ch_16/section_01_04.md new file mode 100644 index 0000000..0035787 --- /dev/null +++ b/scripts/ch_16/section_01_04.md @@ -0,0 +1,501 @@ + +### Chapter 16 : 클래스 안에 클래스가 들어갈 수도 있구나 + +- [`1. 클래스 안의 클래스`](#1-클래스-안의-클래스) +- [`2. static nested 클래스의 특징`](#2-static-nested-클래스의-특징) +- [`3. 내부 클래스와 익명 클래스`](#3-내부-클래스와-익명-클래스) +- [`4. Nested 클래스의 특징은 꼭 알아야 한다`](#4-nested-클래스의-특징은-꼭-알아야-한다) + +--- + +### `1. 클래스 안의 클래스` + +`Java` 에도 클래스 안에 클래스가 들어갈 수 있다. 이러한 클래스를 `"Nested Class"` 라 부른다. `Nested Class` 선언 방식에 따라 `Static Nested Class`, `Inner Class` 로 나눌 수 있다. `Inner Class` 는 말 그대로 클래스 속에 정의된 클래스이고, `Static Nested Class` 는 이를 정적으로 정의한 클래스이다. + +`Nested Class` 형태를 사용하는 가장 큰 이유는 코드를 간단히 표현하기 위함이다. +- 역할에 따라 클래스를 모아두어 코드가 보기 편함 `(Static Nested Class)` +- 캡슐화를 이용해 좀 더 안전한 코드를 만들 수 있음 `(Inner Class)` +- 이를 통해 코드의 가독성과 유지보수성을 높임 + +`Java` 에서 `Nested Class` 는 대개 `Java` 기반의 `UI` 를 처리할 때, 사용자의 입력이나 외부 이벤트에 대한 처리를 할 때 사용된다. + +`Inner Class` 는 또 2 가지로 나눌 수 있는데, `Local Inner Class`, `Anonymous Inner Class` 이다. + +

+ +

+ +`Nested Class` 는 쉽게말해 `"Outer Class 를 어느정도 사용할 수 있는 package"` 라 할 수 있다. + +다음 예시를 보자. + +```java +class OuterClass { + public int OuterValue = 10; + public InnerClass inner = new InnerClass(); + + class InnerClass { + public int InnerValue; + + public void innerMethod() { + System.out.println("can access to OuterValue\t: " + OuterValue); + } + } + + public void outerMethod() { + System.out.println("OuterClass can not access to InnerValue"); + System.out.print("But InnerClass "); + inner.innerMethod(); + } +} + +OuterClass outerClass = new OuterClass(); +OuterClass.InnerClass innerClass = outerClass.new InnerClass(); + +outerClass.OuterValue = 5; +outerClass.outerMethod(); System.out.println(); + +innerClass.innerMethod(); System.out.println(); + +System.out.println("hashCode\tidentityHashCode"); +System.out.println( + String.format("0x%8x", outerClass.inner.hashCode()) + "\t" + + String.format("0x%8x", System.identityHashCode(outerClass.inner)) +); +System.out.println( + String.format("0x%8x", innerClass.hashCode()) + "\t" + + String.format("0x%8x", System.identityHashCode(innerClass)) +); +``` +``` +OuterClass can not access to InnerValue +But InnerClass can access to OuterValue : 5 + +can access to OuterValue : 5 + +hashCode identityHashCode +0x2f2c9b19 0x2f2c9b19 +0x7cca494b 0x7cca494b +``` + +`InnerClass` 에서는 `OuterClass` 의 필드에 접근할 수 있다. 반면 `OuterClass` 에서는 `InnerClass` 의 필드에 접근할 수 없다. + +`innerClass` 객체는 `outerClass` 로 인해 생성되었으므로, `OuterValue` 의 참조가 유지된다. 그래서 `OuterValue` 가 `5` 로 바뀌었음에도 그대로 유지됨을 볼 수 있다. + +`(OuterValue 의 참조만 유지될 뿐, outerClass.inner 인스턴스와 innerClass 객체가 동일하다는 것은 아니다)` + +--- + +### `2. static nested 클래스의 특징` + +`Static Nested Class` 는 `static` 키워드를 이용해 정의한 `Inner Class` 이다. + +하지만 이로 인해 시스템 성능에 큰 차이를 이끌 수 있는데, `Outer Class` 가 `GC` 에 의해 수거될 수도 있고 아닐 수 있기 때문이다. + +일반적으로 `Java` 의 `GC` 는 어느 객체가 `"닿을 수 없는"` `(Unreachable)`, 즉 객체가 어디에서도 참조되지 않을 때 `어느순간` 해당 객체가 존재하는 메모리를 청소한다. + +

+ +

+ +그런데 `Inner Class` 는 항상 어느 `Outer Class` **객체에 대한** 참조를 가지고 있다. 때문에 `Outer Class` **객체**가 다른 객체에 의한 참조가 없더라 해도, `Inner Class` 객체가 어딘가에서 `Reachable` 한 이상 `GC` 에 의해 수거되지 않는다. `(Inner Class, Outer Class 모두 Unreachable 해야 수거된다)` + +반면 `Static Nested Class` 의 경우 `Outer Class` **객체**의 참조를 가지고 있지 않다. 따라서 `Inner Class` 의 경우와 다르게 `GC` 의 수거대상에 올라간다. + +또한 `Static Nested Class` 는 `"Outer Class 에 대한 static 한 참조를 가질 뿐, static 하지 않은"` 점에 유의해야 한다. + +```java +class OuterClassStatic { + public static int OuterValueStatic = 15; + public InnerClassStatc inner = new InnerClassStatc(); + + static class InnerClassStatc { + public int InnerValue; + + public void innerMethod() { + System.out.println("can only access to OuterValueStatic\t: " + OuterValueStatic); + } + } + + public void outerMethod() { + System.out.println("OuterClassStatic can not access to InnerValue"); + System.out.print("But InnerClassStatc "); + inner.innerMethod(); + } +} + +OuterClassStatic outerClassStatic = new OuterClassStatic(); +OuterClassStatic.InnerClassStatc innerClassStatc = new OuterClassStatic.InnerClassStatc(); + +OuterClassStatic.OuterValueStatic = 10; +outerClassStatic.outerMethod(); System.out.println(); + +innerClassStatc.innerMethod(); System.out.println(); + +System.out.println("hashCode\tidentityHashCode"); +System.out.println( + String.format("0x%8x", outerClassStatic.inner.hashCode()) + "\t" + + String.format("0x%8x", System.identityHashCode(outerClassStatic.inner)) +); +System.out.println( + String.format("0x%8x", innerClassStatc.hashCode()) + "\t" + + String.format("0x%8x", System.identityHashCode(innerClassStatc)) +); +``` +``` +OuterClassStatic can not access to InnerValue +But InnerClassStatc can only access to OuterValueStatic : 10 + +can only access to OuterValueStatic : 10 + +hashCode identityHashCode +0x2f2c9b19 0x2f2c9b19 +0x7cca494b 0x7cca494b +``` + +만약 `OuterClassStatic.InnerClassStatc` 클래스가 여타 `static` 필드와 같았다면, `outerClassStatic.inner` 객체와 `innerClassStatc` 객체의 `identityHashCode` 가 동일해야 한다. + +하지만 이 둘은 다르다. `OuterClassStatic.InnerClassStatc` **클래스** 는 `OuterClassStatic` **클래스** 에 대한 참조를 가지고 있을 뿐, 그저 **객체간 참조와 다르다는 것이다.** + +`(OuterValueStatic 는 애초에 그냥 static 필드이므로 이를 공유하는 것은 당연하다)` + +--- + +### `3. 내부 클래스와 익명 클래스` + +#### `내부 클래스` `(Inner Class)` + +앞서 [`1. 클래스 안의 클래스`](#1-클래스-안의-클래스) 에서 `Inner Class` 에 대한 예시를 보았다. `Inner Class` 객체를 생성하기 위해선 다음과 같이 만들 수 있다. + +```java +class OuterClass { + class InnerClass {} +} + +OuterClass outer = new OuterClass(); +OuterClass.InnerClass inner1 = outer.new InnerClass(); +OuterClass.InnerClass inner2 = new OuterClass().new InnerClass(); +``` + +위 문법에서 `Inner Class` 에 대한 특징을 잘 보여준다. `OuterClass` 타입 객체를 생성하지 않고는 `OuterClass.InnerClass` 타입 객체를 생성할 수 없기 때문이다. + +때문에 이를 생성하기 위해선 `OuterClass` 타입 객체를 생성한 후 `outer.new InnerClass()`, `new OuterClass().new InnerClass()` 와 같이 생성할 수밖에 없다. + +`익명 클래스` 의 경우, 교재에서 마음에 드는 정의가 없어 추가로 찾아봤다. 그 결과 `익명 내부 클래스` `(Anonymous Inner Class)` 와 `익명 클래스` `(Anonymous Class)` 가 비슷하면서도 다른 것을 확인하였다. + +--- + +#### `익명 클래스` `(Anonymous Class)` + +`익명 클래스` 는 `interface`, `추상 클래스` 등을 클래스 선언 없이 확장 `(extends)` 하거나 구현 `(implements)` 한 클래스이다. + +```java +interface SomeInterface { + default public void method1() { + System.out.println("SomeInterface method1 origin"); + } + public void method2(); +} + +abstract class SomeClass { + public abstract void method1(); + public void method2() { + System.out.println("SomeClass method2 origin"); + } +} + +SomeInterface anonymousClass1 = new SomeInterface() { + @Override + public void method2() { + System.out.println("SomeInterface method2 modified"); + } +}; + +SomeClass anonymousClass2 = new SomeClass() { + @Override + public void method1() { + System.out.println("SomeClass method1 modified"); + } +}; + +anonymousClass1.method1(); +anonymousClass1.method2(); System.out.println(); +anonymousClass2.method1(); +anonymousClass2.method2(); +``` +``` +SomeInterface method1 origin +SomeInterface method2 modified + +SomeClass method1 modified +SomeClass method2 origin +``` + +이전 `interface`, `추상 클래스` 에서는 이를 상속하여 새로운 클래스를 만들거나 하였다. 하지만 굳이 새로운 클래스를 만들지 않아고 특정 메서드만 `Override` 하면 되는 경우, 위처럼 `익명 클래스` 방법을 사용해 좀더 간결한 코드를 만들 수 있다. + + +--- + +#### `익명 내부 클래스` `(Anonymous Inner Class)` + +`익명 내부 클래스` 는 위 `interface`, `추상 클래스` 등이 `Inner Class` 로 바뀐 것일 뿐이다. + +```java +class OuterClass { + class InnerClass { + public void someInnerMethod() { + System.out.println("Inner Method origin"); + } + } + + public void someMethod(InnerClass input) { + if (input.getClass() != InnerClass.class) return; + System.out.println("Outer Method origin"); + } +} + +OuterClass outer = new OuterClass() { + @Override + public void someMethod(InnerClass input) { + System.out.println("Outer Method modified"); + } +}; +OuterClass.InnerClass anonymousInnerClass = new OuterClass().new InnerClass() { + @Override + public void someInnerMethod() { + System.out.println("Inner Method modified"); + } +}; + +outer.someMethod(new OuterClass().new InnerClass()); +anonymousInnerClass.someInnerMethod(); +``` + +이전 `익명 클래스` 에서와 동일하게 어느 메서드를 `Override` 할 수 있다. + + +이처럼 `"뭔가를 만들어야 됐었는데 만들지 않거나, 객체를 숨기는 방식"` 이기 때문에 `익명` 이라는 단어가 붙은 것 같다. + +--- + +### `4. Nested 클래스의 특징은 꼭 알아야 한다` + +지금까지 클래스의 선언 방식에 따른 `Nested Class` 을 알아 보았다. 그러면서 잠시 `Nested Class` 에서 `Outer Class` 의 필드를 참조하는 것을 보았고, 그 반대는 할 수 없는 것을 보았다. + +이를 제대로 알아보자. + +--- + +#### `Static Nested Class 에서 참조 가능한 범위` + +`Static Nested Class` 는 접근제한자에 상관 않고 `Outer Class` 의 `Static` 필드에만 접근할 수 있다. 이는 `Outer Class` 또한 마찬가지이다. + +```java +class OuterClass { + public String publicScope = "publicScope"; + public static String pubStaticScope = "pubStaticScope"; + + String packScope = "packScope"; + static String packStaticScope = "packStaticScope"; + + protected String proScope = "proScope"; + protected static String proStaticScope = "proStaticScope"; + + private String priScope = "priScope"; + private static String priStaticScope = "priStaticScope"; + + static class StaticNestedClass { + public String publicInnerScope = "publicInnerScope"; + public static String pubStaticInnerScope = "pubStaticInnerScope"; + + String packInnerScope = "packInnerScope"; + static String packStaticInnerScope = "packStaticInnerScope"; + + protected String proInnerScope = "proInnerScope"; + protected static String proStaticInnerScope = "proStaticInnerScope"; + + private String priInnerScope = "priInnerScope"; + private static String priStaticInnerScope = "priStaticInnerScope"; + public void method() { + System.out.println("StaticNestedClass can only access to static fields: "); + System.out.println(pubStaticScope); + System.out.println(packStaticScope); + System.out.println(priStaticScope); + System.out.println(proStaticScope); + // System.out.println(publicScope); compile error: non-static variable priScope cannot be referenced from a static context + // System.out.println(packScope); + // System.out.println(proScope); + // System.out.println(priScope); + } + } + + public void method() { + System.out.println("OuterClass can only access to static inner fields:"); + System.out.println(OuterClass.StaticNestedClass.pubStaticInnerScope); + System.out.println(OuterClass.StaticNestedClass.packStaticInnerScope); + System.out.println(OuterClass.StaticNestedClass.priStaticInnerScope); + System.out.println(OuterClass.StaticNestedClass.proStaticInnerScope); + // System.out.println(OuterClass.StaticNestedClass.publicInnerScope); compile error: non-static variable publicInnerScope cannot be referenced from a static context + // System.out.println(OuterClass.StaticNestedClass.packInnerScope); + // System.out.println(OuterClass.StaticNestedClass.proInnerScope); + // System.out.println(OuterClass.StaticNestedClass.priInnerScope); + } +} + +OuterClass outerClass = new OuterClass(); +OuterClass.StaticNestedClass staticNestedClass = new OuterClass.StaticNestedClass(); + +staticNestedClass.method(); System.out.println(); +outerClass.method(); +``` +``` +StaticNestedClass can only access to static fields: +pubStaticScope +packStaticScope +priStaticScope +proStaticScope + +OuterClass can only access to static inner fields: +pubStaticInnerScope +packStaticInnerScope +priStaticInnerScope +proStaticInnerScope +``` + +만약 `non-static` 한 필드에 접근할 시, `static context` 에서 접근할 수 없다는 컴파일 에러를 뱉어낸다. + +--- + +#### `Inner Class 에서 참조 가능한 범위` + +반면 `Inner Class` 의 경우 많이 다른데, `Inner Class` 에서 `Outer Class` 의 필드를 접근하는데는 아무런 제한도 없지만, `Outer Class` 에서 `Inner Class` 의 경우 상당히 제한된다. + +```java +class OuterClass { + public String publicScope = "publicScope"; + public static String pubStaticScope = "pubStaticScope"; + + String packScope = "packScope"; + static String packStaticScope = "packStaticScope"; + + protected String proScope = "proScope"; + protected static String proStaticScope = "proStaticScope"; + + private String priScope = "priScope"; + private static String priStaticScope = "priStaticScope"; + + class InnerClass { + public String publicInnerScope = "publicInnerScope"; + public static String pubStaticInnerScope = "pubStaticInnerScope"; + + String packInnerScope = "packInnerScope"; + static String packStaticInnerScope = "packStaticInnerScope"; + + protected String proInnerScope = "proInnerScope"; + protected static String proStaticInnerScope = "proStaticInnerScope"; + + private String priInnerScope = "priInnerScope"; + private static String priStaticInnerScope = "priStaticInnerScope"; + public void method() { + System.out.println("InnerClass can access to every field: "); + System.out.println(pubStaticScope); + System.out.println(packStaticScope); + System.out.println(priStaticScope); + System.out.println(proStaticScope); + System.out.println(publicScope); + System.out.println(proScope); + System.out.println(priScope); + } + } + + public void method() { + System.out.println("OuterClass can only access to static inner field bypass:"); + System.out.println(OuterClass.InnerClass.pubStaticInnerScope); + System.out.println(OuterClass.InnerClass.packStaticInnerScope); + System.out.println(OuterClass.InnerClass.proStaticInnerScope); + System.out.println(OuterClass.InnerClass.priStaticInnerScope); + } +} + +OuterClass outerClass = new OuterClass(); +OuterClass.InnerClass innerClass = new OuterClass(). new InnerClass(); + +innerClass.method(); System.out.println(); +outerClass.method(); System.out.println(); + +System.out.println("Outside of class, can not access to private-static field"); +// System.out.println(OuterClass.InnerClass.priStaticInnerScope); compile error: priStaticInnerScope has private access in OuterClass.InnerClass +``` +``` +InnerClass can access to every field: +pubStaticScope +packStaticScope +priStaticScope +proStaticScope +publicScope +proScope +priScope + +OuterClass can only access to static inner field bypass: +pubStaticInnerScope +packStaticInnerScope +proStaticInnerScope +priStaticInnerScope + +Outside of class, can not access to private-static field +``` + +예시를 보면 `InnerClass` 에서는 모든 필드에 접근 가능하다. 하지만 `OuterClass` 에서는 어떠한 인스턴스 필드에 접근이 불가능하고 오직 `static` 필드만 접근 가능하다. + +\+ 사실 위 `OuterClass` 에서 `InnerClass` 필드에 접근할 수 있는 방법이 하나 있다. 그리고 이는 `Static Nested Class` 의 경우에도 적용 가능하다. + +```java +class OuterClass { + public String var = "String origin"; + + static class StaticNested { + private String staticPrivate = "staticPrivate"; + } + class Inner { + private String innerPrivate = "innerPrivate"; + public void method() { + System.out.println(var); + } + } + + public void method() { + System.out.println("Actually, we can access(?) to those fields like this :"); + + OuterClass.StaticNested staticNested = new OuterClass.StaticNested(); + OuterClass.Inner inner1 = new OuterClass(). new Inner(); + OuterClass.Inner inner2 = new Inner(); + + System.out.println(staticNested.staticPrivate); + System.out.println(inner1.innerPrivate); System.out.println(); + + inner1.method(); + inner2.method(); + } +} + +OuterClass outerClass = new OuterClass(); +outerClass.var = "String modified"; + +outerClass.method(); +``` +``` +Actually, we can access(?) to those fields like this : +staticPrivate +innerPrivate + +String origin +String modified +``` + +어찌 생각하면 좀 당연한 논리이다. 새롭게 생성한 객체기는 하지만 여전히 `OuterClass` 안에 있기 때문이다. + +또한 여기서 한가지 새로운 사실을 알아내었는데, `inner1` 와 `inner2` 의 차이이다. + +`inner1` 은 새로운 `OuterClass` 에서 파생된 `Inner` 객체이다. 반면 `inner2` 는 현재 메서드가 실행되고 있는 `OuterClass` 에서 파생된 객체이다. + +때문에 `inner1.method()`, `inner2.method()` 를 비교해보면 서로 다름을 알 수 있다. `outerClass.method()` 를 호출하기 전, `outerClass.var` 의 값을 바꿔, `inner2` 가 참조하는 `var` 가 바뀌었기 때문이다.