1. 程式人生 > >java內部類之成員內部類、區域性內部類和匿名內部類

java內部類之成員內部類、區域性內部類和匿名內部類

內部類概念

一個類中包含另外一個類。

分類

  1. 成員內部類。
  2. 區域性內部類(包含匿名內部類)。

成員內部類

定義格式:

修飾符 class 類名稱 {
    修飾符 class 類名稱 {
        //...
    }
    //...
}

注意:

內部類使用外部,可以隨意訪問,但是外部類使用內部類要藉助內部類的物件。

使用成員內部類

兩種方法:
1. 間接方式:在外部類的方法中,使用內部類,然後main只調用外部類的方法。
2. 直接方式:公式:
一般類:類名稱 物件名 = new 類名稱();
成員內部類:外部內名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();

Body.java含有一個內部類Heart

public class Body {

    private String name;

    //外部類方法
    public void methodBody() {
        System.out.println("外部類的方法");
        new Heart().beat();//在外部類方法中使用內部類。 
    }


    public  class Heart { //成員內部類
        //內部類方法
        public void  beat() {
            System.out.println("心臟跳動"
); System.out.println("我叫:"+name); } } }

下面分別介紹兩種方式訪問內部類:

間接方式訪問內部類

Demo01InnerClass.java

public class Demo01InnerClass {
    public static void main(String[] args) {
        Body body = new Body();//外部類物件

        //通過外部類的物件,呼叫外部類的方法,裡面間接使用內部類Heart
        body.methodBody();

直接方式訪問內部類

public class Demo01InnerClass {
    public static void main(String[] args) {
        Body body = new Body();//外部類對物件

        //外部內名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();
        Body.Heart heart = new Body().new Heart();
        heart.beat();
    }
}

外部類和內部類變數重名問題

當外部類的變數和內部類變數重名時,在內部類方法中訪問外部類變數的格式是:外部類.this.外部類成員變數名

public class Outer {
    int number = 10;

    public class Inner {
        int number = 20;

        public  void methodInner() {
            int number =30;//內部類方法的區域性變數
            System.out.println(number);//就近原則
            System.out.println(this.number);
            System.out.println(Outer.this.number);//外部類的成員變數
        }
    }
}

30
20
10

區域性內部類

如果類是定義在一個方法內部的,那麼這就是一個區域性內部類。
“區域性”:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了。

定義格式

修飾符 class 外部類名稱 {
    修飾符 返回值型別  外部類方法名稱(引數列表) {
        class 區域性內部類名稱 {
            //...
        }
    }
}

Outer.java含有區域性內部類

public class Outer {
    public void methodOuter() {

        class Inner {
            int num = 10;
            public void methodInner() {
                System.out.println(num);//10
            }
        }
        //這個內部列只有當前所屬方法能夠訪問這個類,出了這個方法就不能用了
        Inner inner = new Inner();
        inner.methodInner();
    }
}

DemoMain.java

public class DemoMain {

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.methodOuter();
    }
}

結果:
10

注意:

區域性內部類,如果希望訪問所在方法的區域性變數,那麼這個區域性變數必須是【有效final的】
備註:從java 8+開始,只要區域性變數事實不變,那麼final關鍵字可以省略。
原因:
1. new 出來的物件在堆記憶體當中。
2. 區域性變數是跟著方法走的,在棧記憶體當中。
3. 方法執行結束後,立刻出棧,區域性變數就會立刻消失。
4. 但是new出來的物件在堆當中持續存在,直到垃圾回收消失。

public class MyOuter {
    public void methodOuter() {
        final int num = 10;//所在方法的區域性變數

        /*在棧中的區域性變數可能隨著方法結束出棧消失了,但是建立的內部類物件還存在。
        物件使用常量時能直接將常量copy進來,這時就要保證這個常量是不可更改的,也就是final。
        */
        //num = 20;

        class MyInner {
            public  void methodInner() {
                System.out.println(num);
            }
        }
        Inner myinner = new MyInner();
        inner.methodInner();
    }
}

修飾內部類的許可權

public > protected > (default) > private
定義一個類的時候,許可權修飾符規則:

  1. 外部類:public / (default)
  2. 成員內部類:都可以寫,public > protected > (default) > private
  3. 區域性內部類:什麼都不能寫,但不是de

匿名內部類

如果介面的實現類(或者是父類的子類)只需要使用唯一的一次。
那麼這種情況下就可以省略調該類的定義,而改為使用【匿名內部類】。

匿名內部類定義格式:

介面名稱 物件名 = new 類名或者介面名稱() {
    //覆蓋重寫所有抽象方法
};

代表繼承了或者實現了該介面的子類物件。

==介面MyInterface.java==

public interface MyInterface {
    void method1();//抽象方法
    void method2();
}

==介面實現類MyInterfaceImp.java==

public class MyInterfaceImpl implements  MyInterface {


    @Override
    public void method1() {
        System.out.println("實現類覆蓋重寫了方法1");
    }

    @Override
    public void method2() {
        System.out.println("實現了覆蓋重寫方法2");
    }
}

測試類

一、一般介面實現方法

DemoMain.java

public class DemoMain {
    public static void main(String[] args) {
//        MyInterface impl = new MyInterfaceImpl();
//        impl.method();
        }
}

匿名內部類實現介面

DemoMain.java

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

        //使用匿名內部類,但不匿名物件,物件名稱就叫obj
        MyInterface objA = new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名內部類實現了方法1-A");
            }
            @Override
            public void method2() {
                System.out.println("匿名內部類實現了方法2-A");
            }
        };
        objA.method1();
        objA.method2();

        System.out.println("----------我是分割線----------");

        //使用匿名內部類,而且省略了物件名稱,也是匿名物件
        new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名內部類實現了方法1-B");
            }
            @Override
            public void method2() {
                System.out.println("匿名內部類實現了方法2-B");
            }
        }.method1();
    }
}

結果:
匿名內部類實現了方法1-A
匿名內部類實現了方法2-A
----------我是分割線----------
匿名內部類實現了方法1-B

匿名內部內應用

  • 實現倒計時5秒。
public class Demo02_Time {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("倒計時5秒");
            }
        },5000);//倒計時5秒
    }
}

注意事項:

對格式進行解析:"new 介面名稱 {...};"進行解析

  1. new代表建立物件的動作
  2. 介面名稱就是匿名內部類需要實現那個介面
  3. {…}這才是匿名內部類的內容

注意:

  1. 這個類只需要使用一次。匿名內部類在建立物件時,只能使用唯一一次。
    如果希望多次希望多次建立物件,而且內容一樣的話,那麼就必須使用單獨定義的實現類。

  2. 匿名物件,在呼叫的時候,只能呼叫唯一一次。
    如果希望同一個物件,呼叫多次方法,那麼必須給物件取一個名字。

  3. 匿名內部類是省略了【實現類/子類】,但是匿名物件是省略了【物件命名】。

強調:匿名和匿名物件不是一回事。