首頁 > 軟體

解析JavaSe的內部類

2022-03-02 19:02:46

內部類

1. 內部類簡介

(1) 內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類存取該類。

(2) 內部類成員可以直接存取外部類的私有資料,因為內部類被當成其外部類成員,同一個類的成員之間可以互相存取。但外部類不能存取內部類的實現細節,例如內部類的成員變數。

(3) 匿名內部類適合用於建立那些僅需要一次使用的類。

(4) 在java中內部類主要分為成員內部類(非靜態內部類、靜態內部類)、匿名內部類、區域性內部類。

2. 非靜態內部類

成員內部類是一種與Field、方法、構造器和初始化塊相似的類成員,成員內部類分為兩種:靜態內部類和非靜態內部類,使用static修飾的成員內部類是靜態內部類,沒有使用static修飾的成員內部類是非靜態內部類。

  • 因為內部類作為其外部類的成員,所以可以使用任意存取控制符如private、protected和public等修飾;
  • 非靜態內部類不能有靜態方法、靜態屬性、靜態初始化塊;
  • 非靜態內部類可以直接存取外部類的成員,但是外部類不能直接存取非靜態內部類成員;
  • 外部類的靜態方法、靜態程式碼塊中不能直接建立非靜態內部類範例,存取內部類成員;
  • 非靜態內部類的物件必須寄存在外部類的物件裡,因此建立非靜態內部類物件之前,必須先建立其外部類物件;

當在非靜態內部類的方法記憶體取某個變數時,系統優先在該方法內查詢是否存在該名字的區域性變數,如果存在就使用該變數;如果不存在,則到該方法所在的內部類中查詢是否存在該名字的成員變數,如果存在則使用該成員變數;如果不存在,則到該內部類所在的外部類中查詢是否存在該名字的成員變數,如果存在則使用該成員變數;如果依然不存在,系統將出現編譯錯誤:提示找不到該變數。

(1) 非靜態內部類可以直接存取外部類的成員,但是外部類不能直接存取非靜態內部類成員

public class OuterClass {
    private int a;
    public void test(){
        // 編譯報錯,因為外部類不能直接存取非靜態內部類成員
        System.out.println(b);
        // 如需存取內部類的範例Field,需顯式建立內部類物件
        InnerClass inner = new InnerClass();
        System.out.println(inner.b);
    }
    // 定義非靜態內部類,使用private修飾符
    @Data
    private class InnerClass{
        private int b;
        public void info(){
            // 在非靜態內部類裡可以直接存取外部類的private成員
            System.out.println(a);
        }
    }
}

(2) 非靜態內部類不能有靜態方法、靜態屬性、靜態初始化塊

public class OuterClass {
    // 外部類Field
    private int a;
    // 定義非靜態內部類,使用private修飾符
    @Data
    private class InnerClass{
        // 內部類Field
        private int b;
        private int c;
        // 編譯報錯,非靜態內部類裡面不能有靜態屬性
        private static int d;
        // 編譯報錯,非靜態內部類裡面不能有靜態程式碼塊
        static {
            System.out.println("非靜態內部類裡面不能有靜態程式碼塊");
        }
        // 編譯報錯,非靜態內部類裡面不能有靜態方法
        public static void show(){
            System.out.println("非靜態內部類裡面不能有靜態方法");
        }
    }
}

(3) 外部類的靜態方法、靜態程式碼塊中不能直接建立非靜態內部類範例,存取內部類成員

public class OuterClass {
    private int a;
    public static void test(){
        // 編譯報錯,外部類的靜態方法中無法建立內部類範例
        InnerClass innerClass = new InnerClass();
    }
    static{
        // 編譯報錯,外部類的靜態方法中無法建立內部類範例
        InnerClass innerClass = new InnerClass();
    }
    // 定義非靜態內部類
    @Data
    private class InnerClass{
        private int b;
    }
}

在外部類的普通方法和靜態方法中存取內部類成員

public class OuterClass {
    private int a;
    // 定義非靜態內部類,使用private修飾符
    @Data
    private class InnerClass{
        private int b;
        public void info(){
            System.out.println("內部類的方法info()");
        }
    }
    // 外部類的程式碼塊
    {
        InnerClass innerClass = new InnerClass();
        innerClass.info();
    }
    // 外部類的靜態程式碼塊
    static {
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();
        inner.info();
    }
    // 外部類的普通方法
    public void test(){
        // 在外部類裡使用非靜態內部類時,與平時使用普通類並沒有太大的區別
        InnerClass inner = new InnerClass();
        // 存取內部類的Filed
        System.out.println(inner.b);
        // 存取內部類的方法
        inner.info();
    }
    //  外部類的靜態方法
    public static void test1(){
        // 外部類的靜態方法、靜態程式碼塊中不能直接建立非靜態內部類範例
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();
        // 存取內部類的Filed
        System.out.println(inner.b);
        // 存取內部類的方法
        inner.info();
    }
    // 測試
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.test();
    }
}

3. 靜態內部類

如果使用static來修飾一個內部類,則這個內部類就屬於外部類本身,而不屬於外部類的某個物件。static關鍵字的作用是把類的成員變成類相關,而不是範例相關,即static修飾的成員屬於整個類,而不屬於單個物件。

  • 靜態內部類可以包含靜態成員,也可以包含非靜態成員。
  • 靜態內部類不能存取外部類的範例成員,只能存取外部類的類成員;
  • 外部類不能直接存取靜態內部類的成員,但可以使用靜態內部類的類名作為呼叫者來存取靜態內部類的類成員,也可以使用靜態內部類物件作為呼叫者來存取靜態內部類的範例成員。

(1) 靜態內部類不能存取外部類的範例成員,只能存取外部類的類成員

public class OuterClass {
    private int a;
    private static int b;
    public void test(){
        System.out.println(a);
    }
    @Data
    private static class InnerClass{
        private int c;
        // 靜態內部類中可以包括靜態成員
        private static int d;
        public void info(){
            // 編譯報錯,靜態內部類不能存取外部類的範例成員
            System.out.println(a);
        }
        public static void show(){
            // 靜態內部類可以存取外部類的靜態成員
            System.out.println(b);
        }
    }
}

(2) 外部類不能直接存取靜態內部類的成員,但可以使用靜態內部類的類名作為呼叫者來存取靜態內部類的類成員,也可以使用靜態內部類物件作為呼叫者來存取靜態內部類的範例成員。

public class OuterClass {
    private int a;
    private static int b;
    public void test(){
        // 外部類不能直接存取靜態內部類的成員
        // 可以使用靜態內部類物件作為呼叫者來存取靜態內部類的範例成員
        InnerClass innerClass = new InnerClass();
        innerClass.show();
        // 可以使用靜態內部類的類名作為呼叫者來存取靜態內部類的類成員
        InnerClass.show();
    }
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.test();
    }
    @Data
    private static class InnerClass{
        private int c;
        private static int d;
        public static void show(){
            System.out.println(b);
        }
    }
}

4. 匿名內部類

匿名內部類適合建立那種只需要一次使用的類,建立匿名內部類時會立即建立一個該類的範例,這個類定義立即消失,匿名內部類不能重複使用。

  • 匿名內部類必須繼承一個父類別,或實現一個介面,但最多隻能繼承一個父類別,或實現一個介面;
  • 匿名內部類不能是抽象類,因為系統在建立匿名內部類時,會立即建立匿名內部類的物件。因此不允許將匿名內部類定義成抽象類;
  • 匿名內部類不能定義構造器,因為匿名內部類沒有類名,所以無法定義構造器,但匿名內部類可以定義範例初始化塊,通過範例初始化塊來完成構造器需要完成的事情;
public interface Product {
     public int getPrice();
     public String getName();
}
public class Test {
    public void test(Product product){
        System.out.println("name:"+product.getName() +"-------"+"name:"+product.getPrice());
    }
    public static void main(String[] args) {
        Test test = new Test();
        test.test(new Product() {
            @Override
            public int getPrice() {
                return 12;
            }
            @Override
            public String getName() {
                return "蘋果";
            }
        });
    }
}

Test類定義了一個test方法,該方法需要一個Product物件作為引數,但Product只是一個介面,無法直接建立物件,因此此處考慮建立一個Product介面實現類的物件傳入該方法——如果這個Product介面實現類需要重複使用,則應該將該實現類定義成一個獨立類;如果這個Product介面實現類只需一次使用,則可採用上面程式中的方式,定義一個匿名內部類。

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!   


IT145.com E-mail:sddin#qq.com