1. 程式人生 > >Java內部類使用總結

Java內部類使用總結

一、內部類的定義和作用

將一個類定義在另一個類裡面或者方法裡面的類成為內部類。
作用和優點:每個內部類都能獨立的繼承一個介面的實現,所以無論外部類是否繼承了某個介面的實現,對於內部類都沒有影響。內部類使得多繼承的解決方案變得完整。彌補了單繼承的缺點

二、內部類的用法

1.成員內部類

成員內部類即為外部類的成員,非靜態的。可以直接訪問外部類的所有成員和方法,當有和內部類重名時需要用外部類.this.屬性或方法。外部類訪問內部類需要建立一個內部類的物件才可以訪問。PS:成員內部類不能含有static的修飾符。因為內部類屬於非靜態的,先要建立外部類,才能建立它自己。成員內部類的修飾符可以是private,預設的,protected,public或者是靜態的。和外部類(public和default)不同

public class Outer {

    public static void main(String[] args){
        Outer outer = new Outer();
        Outer.Inner inner = outer.getInner();
        inner.print("成員內部類第一次列印");
        inner = outer.new Inner();
        inner.print("這是一成員內部類");
    }

    public Inner getInner(){
        return
new Inner(); } public static void printOuter(){ System.out.println("這是一個外部類"); } public class Inner{ public void print(String str){ getInner(); Outer.this.printOuter(); System.out.println(str); } } }

2.區域性內部類

區域性內部類定義在方法或作用域內部。

public class Part {

    public Destination print(String str){
        class a implements Destination{

            private String label;
            private a(String label){
                this.label = label;
            }
            public String read() {
                return label;
            }
        }
        return new a(str);
    }

    public static void main(String[] args){
        System.out.println(new Part().print("這是一區域性內部類的事例").read());
    }
}

3.靜態內部類

靜態內部類可以讓外部類隨意訪問,無需建立內部類的物件。靜態內部類不能訪問外部類的非靜態成員,因為非靜態成員必須是物件存在的時候,所以有矛盾。

public class Singleton2 {
    // 提供一私有建構函式
    private Singleton2(){};
    public Integer i;
    public static Singleton2 getInstance(){
        return SingletonHolder.instance;
    }

    private static class SingletonHolder{
        private static final Singleton2 instance = new Singleton2();
    }
}

4.匿名內部類

匿名內部類也可以進行例項初始化,雖然沒有建構函式

public class Outer {

    public static void main(String[] args){
        Outer outer = new Outer();
        Outer.Inner inner = outer.getInner("Inner", "gz");
        System.out.println(inner.getName());
        System.out.println(inner.getProvince());
    }

    public Inner getInner(final String name, final String city){
        // 匿名內部類中使用
        return new Inner() {
            private String namString = name;
            private String province;
            // 例項初始化
            {
                if(city.equals("gz")){
                    province = "gd";
                }else {
                    province = "";
                }
            }

            public String getName() {
                return namString;
            }

            public String getProvince() {
                return province;
            }
        };
    }

    interface Inner{
        String getName();
        String getProvince();
    }
}

5.內部類的繼承

注意的是繼承內部類的類的建構函式預設傳遞外部類引數,並呼叫super方法,不然會有編譯錯誤。

class InheritInner extends Outer.Inner {

    public InheritInner(Outer outer){
        outer.super();
    }

    public static void main(String[] args){
        new InheritInner(new Outer()).print("這是內部類繼承");
    }
}

三、注意的問題

1.為什麼成員內部類可以無條件的訪問外部類成員:

首先要明確一點的是內部類和外部類是兩份位元組碼,即Outer.class, Outer$Inner.class.
通過反編譯位元組碼可以看出,內部類的構造器是無參建構函式,編譯器會預設新增一個引數,改引數的型別為指向外部類物件的一個引用,所以成員內部類中的Outter this&0 指標便指向了外部類物件,因此可以在成員內部類中隨意訪問外部類的成員。

2.為什麼匿名內部類和區域性內部類只能訪問區域性的final。

public class Test {
    public static void main(String[] args)  {

    }

    public void test(final int b) {
        final int a = 10;
        new Thread(){
            public void run() {
                System.out.println(a);
                System.out.println(b);
            };
        }.start();
    }
}

如上面的程式碼,因為是當test方法執行完畢之後,變數a的生命週期就結束了,而此時Thread物件的生命週期很可能還沒有結束,那麼在Thread的run方法中繼續訪問變數a就變成不可能了,但是又要實現這樣的效果,怎麼辦呢?Java採用了 複製,也就說如果區域性變數的值在編譯期間就可以確定,則直接在匿名內部裡面建立一個拷貝。如果區域性變數的值無法在編譯期間確定,則通過構造器傳參的方式來對拷貝進行初始化賦值。 的手段來解決這個問題。複製的手段會帶來一個問題,會導致資料不一致,所以採用final為不可變