Inner Class(내부 클래스)
1. Member inner class(멤버 내부 클래스): 다른 클래스 내부에서 선언된 클래스
2. Static inner class(static 내부 클래스): 다른 클래스의 내부에서 static으로 선언된 클래스
3. Local class(지역 클래스)
public class Inner01Main {
    public static void main(String[] args) {
       System.out.println("Member Inner Class(멤버 내부 클래스)");
       // 외부 클래스의 인스턴스 생성
       TestOuter out = new TestOuter(100);
       // 멤버 내부 클래스의 인스턴스 생성
       // 멤버 내부 클래스의 이름: [외부클래스 이름].[멤버 내부클래스 이름]
       // [외부클래스 이름].[내부클래스 이름] 참조변수 =
       //      [외부클래스 인스턴스].new 내부클래스 생성자();
       TestOuter.TestInner in=out.new TestInner(111);
       in.printOuterValue(); //100
       in.printInnerValue(); //111
       // 하나의 외부 클래스 인스턴스를 이용해서
       // 멤버 내부 클래스의 인스턴스는 여러개를 생성할 수 있다.
       System.out.println();
       TestOuter.TestInner in2 = out.new TestInner(123);
       in2.printOuterValue(); //마찬가지로 동일한 out 쓰는 중 : 100
       in2.printInnerValue(); //123
       System.out.println();
       TestOuter.TestInner in7;
       in7 = new TestOuter(30).new TestInner(330);
       in7.printOuterValue(); //30
       in7.printInnerValue(); //330
    } // end main()
} // end class Inner01Main// 클래스: 멤버 변수들 (+ 생성자들) + 멤버 메소드들 = 데이터 타입
public class TestOuter {
    // 멤버 변수:
    private int outerValue;
    // 생성자:
    public TestOuter() {
    }
    public TestOuter(int outerValue) {
        this.outerValue = outerValue;
    }
    // 메소드:
    public int getValue() {
        return outerValue;
    }
    public void setValue(int outerValue) {
        this.outerValue = outerValue;
    }
    // Inner class 정의
    // (수식어) class 클래스이름 {...}
    public class TestInner {
        //필드
        private int innerValue;
        //생성자
        public TestInner() {
        }
        public TestInner(int innerValue) {
            this.innerValue = innerValue;
        }
        //메소드
        public void printOuterValue() {
            //내부 클래스는 외부 클래스의 필드 직접 접근 가능
            System.out.println("outerValue: " + outerValue);
        }
        public void printInnerValue() {
            System.out.println("innerValue: " + innerValue);
        }
    }//end inner class
} // end class TestOuter
상속 관계로 묶을 수는 없지만, A라는 객체가 생성된 이후에야 존재할 수 있는 B라는 객체가 있다고 한다면, B를 A의 멤버 '내부 클래스'로 선언한다.
ex) 컴퓨터-CPU / 메모리, 자동차-타이어
반면, '햄버거 세트 메뉴' 객체의 경우, 햄버거 객체와 콜라 객체는 별개의 객체로도 존재 가능하니까 '햄버거' 와 '콜라' 는 '세트메뉴' 객체의 '멤버변수'로 붙도록 하는게 낫다.
is-a : 상속관계
has-a (종속) : 멤버내부클래스
has-a (독립) : 멤버변수
다른 클래스의 내부에서 멤버로 정의된 클래스인데, static 키워드가 사용된 내부 클래스 (static inner class)
static:
중첩 클래스의 인스턴스 생성:
effectively final 변수란?
1. final로 선언된 변수, 또는
2. 한 번 초기화가 된 이후로 값이 변경되지 않은 변수(Java 8에서 도입)
public class Local01Main {
    public static void main(String[] args) {
       System.out.println("Local Inner Class(지역 내부 클래스)");
       TestOuter out=new TestOuter();
       out.localMethod(400);
       
    } // end main()
} // end class Local01Mainpublic class TestOuter {
    //TestOuter 클래스의 멤버 변수
    private int num1 = 100; // ①
    // TestOuter 클래스의 멤버 메소드
    public void localMethod(final int num4) {
        int num2 = 200; //localMethod() 지역변수
//        num4 *= 2; //오류: final임
        // LocalMethod 메소드 안에 정의된 Local inner class
        class TestLocal {
            private int num3 = 300; // Local class의 필드
            //class에 접근제어자 못 붙임. 내부는 괜찮음
            public void showNumbers() {
                System.out.println("num1 = " + num1);
                System.out.println("num2 = " + num2);
                System.out.println("num3 = " + num3);
                System.out.println("num4 = " + num4);
            }
        }
        TestLocal local = new TestLocal();
        //num2 = 400;
        // num2 값을 변경하면.. 아래 showNumbers()에선
        // 200 이 찍혀야 하나? 400이 찍혀야 하나?
        // 그래서 로컬내부클래스에서 사용 가능한 지역의 변수는
        // 반드시 effectively final 이어야 한다
        //       즉 한번 초기화 된후 값이 변경되지 않거나, final 이어야 한다.
        local.showNumbers();
    } // end localMethod()
} // end class TestOuter