1. 程式人生 > 實用技巧 >故鄉魂

故鄉魂

https://bbs.pku.edu.cn/v2/post-read.php?bid=459&threadid=17733059

package com.example.nestedclassdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 巢狀類 Nested classes
 * - 靜態巢狀類: Static nested classes,即類前面有static修飾符
 * - 非靜態巢狀類: Non-static nested classes,又名內部類,Inner classes
 *  - 普通內部類(亦翻譯為:成員內部類)
 *  - 區域性內部類(Local classes)
 *  - 匿名內部類(Anonymous classes)
 *
 *  為什麼需要巢狀類
 *  - 不同的訪問許可權要求,更細粒度的訪問控制
 *  - 簡潔,避免過多的類定義
 *  - 語言設計過於複雜,較難學習和使用
 *
 *  匿名內部類:Anonymous classes
 *  - 沒有類名的內部類,必須繼承一個父類/實現一個父介面
 *  - 在例項化以後,迅速轉型為父類/父介面
 *  - 這種型別的物件,只能new一個物件,之後以物件名字操作
 *  - 可在普通語句和成員變數賦值時使用內部類
 *
 *  區域性內部類:Local classes
 *  - 定義在程式碼塊中的非靜態的類,如方法,for迴圈,if語句等
 *  - 定義後,即可建立物件使用
 *  - 只能活在這個程式碼塊中,程式碼塊結束後,外界無法使用該類
 *
 *  瞭解匿名內部類和區域性內部類
 *  - 兩者幾乎相似
 *  - 區域性內部類可以重用,匿名內部類不能重用
 *  - 匿名內部類更簡潔
 *
 *  普通內部類
 *  - 非static的類,定義在某個類的成員變數位置
 *  - 定義後,在類裡面均可以使用
 *
 *  靜態巢狀類
 *  - 層級和包圍類(enclosing class)的成員變數/方法一樣
 *  - 第三方需要通過外部包圍類才可以訪問到靜態巢狀類
 *
 *  瞭解普通內部類和靜態巢狀類
 *  - 兩者都定義在外圍類中的成員級別
 *  - 靜態巢狀類不依賴於外圍類物件,但是隻能訪問外圍類的靜態成員
 *  - 普通內部類必須依賴於外圍類物件,不能單獨存在,但是可以訪問外圍類的所有成員
 *
 *  四種類對比(1)
 *  Oracle官方文件比較
 *  - 匿名內部類:應用它,如果需要定義額外的變數和方法
 *  - 區域性內部類:在一個方法內,需要建立一個新的型別,並重復使用
 *  - 普通內部類:和區域性內部類相似,在一個類中定義,可重複使用,可以訪問外部類成員,但不需要訪問外部類方法的形參和內部變數
 *  - 靜態巢狀類:在一個類中定義,可重複使用,並需要訪問外部類的靜態成員
 *
 *  外部訪問規則
 *  外部訪問和修飾符關係
 *  - 普通內部類和靜態巢狀類可以被外部訪問
 *  - 外部訪問普通內部類和靜態巢狀類,和普通類之間訪問規則一樣
 *
 *  變數遮蔽 Shadowing
 *  - 巢狀類變數和外部包圍類的變數重名
 *   - 以離得近作為優先原則
 *   - 優先順序高的變數會遮蔽優先順序低的變數
 *   - 外部包圍類.this.變數名,可以訪問到外部包圍類的成員變數
 *   - 靜態巢狀類不能訪問非靜態變數
 *   - Java7及以前,匿名內部類和區域性內部類只能訪問外部包圍類的final成員變數
 *   - Java8及以後,匿名內部類和區域性內部類可訪問外部包圍類的final成員變數和事實意義上的final變數
 *
 *   
 *
 *
 *
 *
 *
 *
 */
@SpringBootApplication
public class NestedclassDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(NestedclassDemoApplication.class, args);
    }

}

package com.example.nestedclassdemo;

/**
 * 匿名內部類
 * - 沒有正式類名的內部類
 *  - 編譯器產生內部名字: 類名+$+數字編號
 * - 沒有類名,沒有建構函式,能用父類/父介面的建構函式(可帶引數)
 * - 可以繼承,改寫,補充父類/父介面的方法
 * - 內部不可以新定義靜態成員(變數+方法),常量除外
 *  - final static int a = 5;
 * - 可以訪問外部包圍類的成員變數和方法(包括private)
 * - 如果定義在靜態方法中,也只能訪問外部包圍類的靜態成員
 * - 沒有類名,外部包圍類和其他類也無法訪問到匿名內部類
 */
public class Outer1 {
    private String name = "abc";

    public void f1(){
        String name = "def";

        Runnable r = new Runnable() {
            //匿名內部類不能定義靜態變數,除非是常量
            public final static int a = 5;
            //public static int b = 3;
            String name = "ghi";
            @Override
            public void run() {
                System.out.println("hello "+name);
            }
            //靜態方法不能在匿名內部類定義
            //public static void f2(){}
        };
        new Thread(r).start();
        System.out.println(r.getClass().getName());

        //編譯後會產生第二個class匿名內部類
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello "+Outer1.this.name);
            }
        };
        new Thread(r2).start();
        System.out.println(r2.getClass().getName());
    }
}

package com.example.nestedclassdemo;

import java.io.Serializable;

/**
 * 區域性內部類: Local classes
 * - 編譯後名稱: 外部類名+$+序號+內部類名
 * - 可以繼承其他類,或者實現其他介面
 * - 非靜態的類,不能包含靜態成員(變數和方法),除了常量
 * - 可以訪問外部包圍類的成員
 * - 如果定義在靜態方法中,只能訪問包圍類的靜態成員
 * - 區域性內部類不能是一個介面,即介面不能定義在程式碼塊中
 */
public class Outer2 {
    String name = "abc";

    //區域性內部類
    public void f1(){
        String name = "def";

        class Inner2 extends Outer1 implements Runnable, Serializable {
            final static int a = 1;
            //不允許定義靜態普通類成員
            //static int b = 2;
            String name = "ghi";

            public void f2(){
                System.out.println(name);
                System.out.println(Outer2.this.name);
            }

            @Override
            public void run() {
                System.out.println(name);
            }
        }

        Inner2 obj1 = new Inner2();
        obj1.f2();
        System.out.println(obj1.getClass().getName());
    }

    //區域性內部類
    public static void f2(){
        final String name = "def";

        class Inner2{
            public String f2(){
                return "this is f2()";
            }
        }

        Inner2 inner2 = new Inner2();
        String s = inner2.f2();
        System.out.println(s);
        System.out.println(inner2.getClass().getName());
    }
}

package com.example.nestedclassdemo.shadow;

public class ShadowTest {

    public int x = 0;

    class FirstLevel{
        public int x  = 1;

        void methodInFirstLevel(int x){
            System.out.println("x = "+x); //line 10 x
            System.out.println("this.x = "+this.x); //line 8 x
            System.out.println("ShadowTest.this.x = "+ShadowTest.this.x); //line 5 x
        }
    }

    public static void main(String... args){
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel f1 = st.new FirstLevel();
        f1.methodInFirstLevel(20);
    }
}

package com.example.nestedclassdemo.shadow;

public class ShadowTest2 {

    public int x = 0;

    public void f1(){
        //區域性內部類無法訪問得到!
        int x = 20;

        class FirstLevel{
            public int x = 1;

            void methodInFirstLevel(int x){
                System.out.println("x = "+x); //line 14 x
                System.out.println("this.x = "+this.x); //line 12 x
                System.out.println("ShadowTest.this.x = "+ShadowTest2.this.x); //line 5 x
            }
        }

        FirstLevel obj = new FirstLevel();
        obj.methodInFirstLevel(10);
    }

    public static void main(String... args) {
        ShadowTest2 st = new ShadowTest2();
        st.f1();
    }
}

package com.example.nestedclassdemo.shadow;

public class ShadowTest3 {

    public int x = 0;

    public void f1(){
        //可以訪問得到,沒有被遮蔽!
        int x = 20;

        class FirstLevel{
            void methodInFirstLevel(){
                System.out.println("x = "+x);//line 9 x
                System.out.println("ShadowTest3.this.x = "+ShadowTest3.this.x); //line 5 x
            }
        }

        //x=30;
        FirstLevel obj = new FirstLevel();
        obj.methodInFirstLevel();
    }

    public static void main(String... args) {
        ShadowTest3 st = new ShadowTest3();
        st.f1();
    }
}

package com.example.nestedclassdemo.outer3;

public class Animal {

    String name;

}

package com.example.nestedclassdemo.outer3;

public interface Flyable {

    void fly();

}

package com.example.nestedclassdemo.outer3;

/**
 * 普通內部類
 * - 編譯後名稱: 外部類名+$+內部類名
 * - 可以繼承其他類,或者實現其他介面
 * - 可以用private/package private(不寫)/protected/public控制外界訪問
 * - 非靜態的類,不能包含靜態變數/方法,除了常量
 * - 和外部包圍類的例項相關,一個普通內部類例項肯定是在一個外部包圍類的例項中,
 * 且可以訪問外部包圍類的所有成員
 * - 在第三方類中,需要先建立外部包圍類例項,才能建立普通內部類的例項,
 * 不允許單獨的普通內部類物件存在!!!
 */
public class Outer3 {
    String name = "aaaaaa";

    public class Bird extends Animal implements Flyable{
        //常量OK
        public static final int a = 3;
        //不能定義非常量的static變數
        //public static int b = 4;

        @Override
        public void fly() {
            System.out.println(Outer3.this.name+" can fly");
        }
    }

    public Bird b1 = new Bird();

    public void f1(){
        b1.fly();
        System.out.println(b1.getClass().getName());
        this.name = "bbbbbb";
        b1.fly();
    }

    public Bird getBird(){
        return this.b1;
    }
}

package com.example.nestedclassdemo.outer3;

public class Outer3Test {

    public static void main(String[] args) {
        Outer3 foo1 = new Outer3();
        foo1.f1();

        Outer3.Bird foo2 = foo1.new Bird();
        foo2.fly();
        System.out.println(foo2 == foo1.getBird());

        //foo1物件下,有2個內部類物件和它關聯

        //不允許沒有關聯的單獨的普通內部類物件
        //Outer3.Bird foo3 = new Outer3().Bird();
    }
}

package com.example.nestedclassdemo.outer4;

/**
 * 靜態巢狀類的語法總結
 * - 需要加修飾符static
 * - 可以定義靜態成員和非靜態成員
 * - 不能直接訪問包圍類的非靜態成員,可直接訪問包圍類的靜態成員
 *  = 可通過包圍類的物件進行訪問非靜態成員
 * - 外界可以通過靜態巢狀類名訪問其靜態成員,通過物件訪問其非靜態成員
 * - 外界需要通過包圍類才可以訪問到靜態巢狀類,並建立其物件,不需要外部包圍類的例項
 */
public class Outer4 {
    private String outField1 = "outer 111";
    private static String outStaticField2 = "outer static 222";

    /**
     * 靜態巢狀類
     */
    public static class Inner4{
        //靜態巢狀類可以定義靜態和非靜態成員
        private String innField1 = "inner 333";
        static String innStaticField2 = "inner static 444";

        public void innFun1(){
            //靜態巢狀類可以直接訪問包圍類的靜態成員
            System.out.println(innField1);
            //System.out.println(Outer4.outField1);
            System.out.println(Outer4.outStaticField2);
            System.out.println(outStaticField2);

            //靜態巢狀類可以通過物件訪問包圍類的非靜態成員
            Outer4 outObj = new Outer4();
            System.out.println(outObj.getOutField1());
        }
        public String getInnField1(){
            return this.innField1;
        }
        public static String getInnStaticField2(){
            hello();
            return innStaticField2;
        }
        public static void hello(){
            System.out.println("inner hello");
        }
    }
    public String getOutField1(){
        return this.outField1;
    }
    public static void outFun2(){
        Inner4 obj1 = new Inner4();
        Inner4 obj2 = new Inner4();
        System.out.println(obj1==obj2);//false
        System.out.println(obj1.getInnField1());
        System.out.println(Inner4.getInnStaticField2());
    }
    public static void hello(){
        System.out.println("outer hello");
    }

}

package com.example.nestedclassdemo.outer4;

public class Outer4Test {

    public static void main(String[] args) {
        //第三方類訪問靜態巢狀類
        Outer4.Inner4 obj1 = new Outer4.Inner4();
        String innField1 = obj1.getInnField1();
        System.out.println("innField1:"+innField1);

        Outer4.Inner4 obj2 = new Outer4.Inner4();
        System.out.println(obj1==obj2);

        System.out.println("====================");
        Outer4 obj3 = new Outer4();
        Outer4.outFun2();
    }
}