天天看點

java 内部類、匿名内部類

一:内部類

1:什麼是内部類?

  大部分時候,類被定義成一個獨立的程式單元。在某些情況下,也會把一個類放在另一個類的内部定義,這個定義在其他類内部的類就被稱為内部類(有些地方也叫做嵌套類),包含内部類的類也被稱為外部類(有些地方也叫做宿主類)

我們先建立一個基本的内部類結構:

class Outer{//外部類
    //内部類
    class Inner{
        
    }
}      

2:内部類的劃分

内部類分為成員内部類和局部内部類。内部類也會生成.class檔案。

java 内部類、匿名内部類

2.1: 成員内部類

  定義在外部類中的成員位置,與類中的成員變量相似,可通過外部類對象進行通路。

java 内部類、匿名内部類

 内部類可以使用外部類的成員,包括私有成員。但是外部類要使用内部類的成員,必須建立内部類變量。

2.2: 局部内部類(比較少用)

定義:在方法裡面有一個内部類。

java 内部類、匿名内部類

 隻有在内部類所屬的方法中建立内部類對象,方可通路局部内部類。而測試類中隻需要建立外部類對象,然後調用外部類方法即可。

3:例子

import java.util.HashMap;

public class Parcell {

    private HashMap<String, String> testMap = new HashMap<String, String>();

    class Contents {
        // 傳回一個外部類的引用.
        public Parcell ParcellRef = Parcell.this;
    }

    class Destination {
        public void putSomethingInMap() {
            testMap.put("hello", "world");
            System.out.println(testMap.get("hello"));
        }
        
    }

    public Destination to() {
        return new Destination();
    }

    public Contents contents() {
        return new Contents();
    }

    public void ship(String dest) {
        Contents c = new Contents();
        Destination d = new Destination();
    }

    public static void main(String[] args) {
        Parcell p = new Parcell();
        Parcell.Contents c = p.contents();
        Parcell.Destination d = p.to();
        d.putSomethingInMap();
        Parcell.Contents c1 = p.new Contents();
    }

}      

内部類的文法介紹

  (1)普通内部類持有一個指向外部類的引用。要建立普通内部類,一定要先建立外部類。

  (2)普通内部類就像人體的心髒一樣,能夠随意通路外部類的任意成員變量。

  (3)在内部類中可以通過“外部類類名.this”的方式傳回一個指向外部類執行個體的引用.如Parcell.this

  (4)在外部類的static方法中若要建立内部類對象,則需要通過“外部類類名.new XXX()”的方式來建立。

  (5)普通内部類中不能擁有靜态成員變量。靜态内部類中可以擁有靜态成員變量。也可以擁有非靜态成員變量。但是靜态内部類不能通路外部類中非靜态的成員變量。而普通内部類可以通路外部類的靜态成員變量。

為什麼static方法中需要p.new XXX()的方式而非static方法中我們直接new 内部類名 就可以建立一個對象了呢?

如果你有這樣的疑問請再看看第一條,一定可以想明白的。

4.作用

  1)更好的封裝性

  2)内部類成員可以直接通路外部類的私有資料,因為内部類被當成其外部類成員,但外部類不能通路内部類的實作細節,例如内部類的成員變量

  3)匿名内部類适合用于建立那些僅需要一次使用的類

體外話:靜态内部類

  Java裡面static一般用來修飾成員變量或函數。但有一種特殊用法是用static修飾内部類,普通類是不允許聲明為靜态的,隻有内部類才可以。被static修飾的内部類可以直接作為一個普通類來使用,而不需執行個體一個外部類。

靜态内部類的特點:

  1.非靜态内部類中不允許定義靜态成員

  2.外部類的靜态成員不可以直接使用非靜态内部類

  3.靜态内部類,不能通路外部類的執行個體成員,隻能通路外部類的類成員

二:匿名内部類

匿名内部類使用最頻繁的場合就是在建立線程的時候。

程式清單2-1:

public class Demo {

    public void test(String title) {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                // title = "我不要吃雞";
                // 改變時會提示錯誤
                // 在封閉範圍中定義的局部變量必須是final的。
                System.out.println(title);
            }
        });
        thread.start();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Demo demo = new Demo();
            demo.test("我要吃雞" + i);
        }
    }
    
}      

    在程式清單2-1中,test()方法内部有一個線程對象thread,是通過new Thread()建立的。

new Thread()

可以接收一個實作了Runnable接口類型的對象,這個對象要怎麼建立呢?可以通過匿名内部類的形式來建立——

new Runnable() {public void run(){......}}

——這段簡短的代碼等同于:

// 實作Runnable接口
class MyRunnable implements Runnable {

    @Override
    public void run() {
        
    }
}

// 向上轉型
Runnable myRunnable = new MyRunnable();      

匿名内部類的好處就在于不僅節省了定義實作類的過程,還能夠自動向上轉型。

  在程式清單2-1中,test()方法還有一個參數title,JDK1.8之前,編譯器要求它必須是final類型的。但JDK1.8之後,如果我們在匿名内部類中需要通路局部變量,那麼這個局部變量不再需要用

final

關鍵字修飾了。

  但如果想要在匿名内部類中改變局部變量的值,編譯器就會提醒你不能這樣做,它會提示:“在封閉範圍中定義的局部變量必須是final的。”

另一個關于匿名内部類的例子:

開發中,最常用到的内部類就是匿名内部類了。以接口舉例,當你使用一個接口時,似乎得做如下幾步操作。

   1、定義子類

   2、重寫接口中的方法

   3、建立子類對象

   4、調用重寫後的方法

我們的目的,最終隻是為了調用方法,那麼能不能簡化一下,把以上四步合成一步呢?匿名内部類就是做這樣的快捷方式。

條件

  匿名内部類必須繼承一個父類或者實作一個父接口。

格式

new 父類名或者接口名(){
    // 方法重寫
    @Override 
    public void method() {
        // 執行語句
    }
};      

使用方式

以接口為例,匿名内部類的使用,代碼如下:

定義接口:

public abstract class FlyAble{
    public abstract void fly();
}      

建立匿名内部類,并調用:

public class InnerDemo {
    public static void main(String[] args) {
        /*
        1.等号右邊:是匿名内部類,定義并建立該接口的子類對象
        2.等号左邊:是多态指派,接口類型引用指向子類對象
        */
        FlyAble  f = new FlyAble(){
            public void fly() {
                System.out.println("我飛了~~~");
            }
        };

        //調用 fly方法,執行重寫後的方法
        f.fly();
    }
}      

通常在方法的形式參數是接口或者抽象類時,也可以将匿名内部類作為參數傳遞。代碼如下:

public class InnerDemo2 {
    public static void main(String[] args) {
        /*
        1.等号右邊:定義并建立該接口的子類對象
        2.等号左邊:是多态,接口類型引用指向子類對象
       */
        FlyAble  f = new FlyAble(){
            public void fly() {
                System.out.println("我飛了~~~");
            }
        };
        // 将f傳遞給showFly方法中
        showFly(f);
    }
    public static void showFly(FlyAble f) {
        f.fly();
    }
}      

以上兩步,也可以簡化為一步,代碼如下:

public class InnerDemo3 {
    public static void main(String[] args) {           
        /*
           建立匿名内部類,直接傳遞給showFly(FlyAble f)
        */
        showFly( new FlyAble(){
            public void fly() {
                System.out.println("我飛了~~~");
            }
        });
    }

    public static void showFly(FlyAble f) {
        f.fly();
    }
}      

為什麼需要内部類?

  Java的内部類讓我很容易的想起來JavaScript的閉包,閉包就是定義在一個函數内部的函數——這聽起來和Java的内部類定義一樣一樣的。本質上,閉包是将函數内部與函數外部連接配接起來的橋梁。内部類一樣,它是将内部類與外部類連接配接起來的橋梁。

來看看什麼是閉包吧:

function wanger() {
    var age = 30;
    function know() {
        console.log(age);
    }
}

wanger();
// 控制台輸出30      

除此之外,内部類最引人注意的原因是:

 内部類可以獨立地繼承一個抽象類或者實作一個接口,無論外部類是否也這樣做了,對内部類都沒有影響。