程序員你真的理解匿名內部類嗎?

前言 嘿,java學習者,問你兩個問題:若是提到線程你會不會立馬想到接口和繼承?若是提到接口和繼承你會不會立馬想到匿名內部類? @[toc]javascript

開篇甜點

爲了加深各位對匿名內部類的印象、好奇心以及求知的渴望,咋們先來看一個程序java

package AnonymousInner;

public class NiMingInnerClassThread {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i<5;i++){
                    System.out.println("熊孩子:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i = 0; i < 5 ; i++){
            System.out.println("傻狍子:"+i);
        }
    }
}

看不懂不要緊啦,看標題就知道了,這只是餐前甜點,這樣你纔會更有胃口去感覺匿名內部類的魅力小程序

唉唉唉...是否是感受這個寫法好像有點不正常啊?但事實上,java確實能夠這樣玩,這個寫法正是匿名內部類方式實現線程的建立~不懂線程不要緊,以後博主應該能抽個空出來寫一篇線程文章~,這個程序就涉及到了這篇文章的主要核心,那就是匿名內部類。多線程

何謂匿名內部類?

正所謂匿名內部類,正如它的字面意思同樣,也就是沒有名字的內部類。也是由於沒有名字因此<font color=red>匿名內部類只能執行一次</font>,想應該也能想到這一點吧。匿名內部類一般用簡化代碼編寫,咱們平時寫代碼都不多用簡化代碼,因此看着奇怪也不足爲奇。框架

爲何要使用匿名內部類?

至於爲何要使用匿名內部類這個問題,我以爲先看下面程序以後,會更好的體會:eclipse

普通實現抽象方法(不使用匿名內部類)ide

package AnonymousInner;

abstract class Father{
    public abstract void speak();
}

class Son extends Father{

    @Override
    public void speak() {
        System.out.println("熊孩子:粑粑,我想哦粑粑");
    }
}

public class NIMingDemo {
    public static void main(String[] args) {
        Father f=new Son();
        f.speak();

    }
}
        
運行結果:
        熊孩子:粑粑,我想哦粑粑

能夠看到,咱們用Son繼承了Father類,而後實現了Son的一個實例,將其向上轉型爲Father類的引用。 可是,若是此處的Son類只使用一次,那麼將其編寫爲獨立的一個類豈不是很麻煩?這個時候就應該想到匿名內部類了,匿名內部類就此誕生了!因此咱們就可使用匿名內部類來實現抽象方法,具體操做在下面一節詳細講解。工具

注意:若是對多態的向上轉型概念不夠清晰的童鞋能夠看看下面這篇文章,下面這篇文章裏面詳細的介紹了多態的向上向下轉型以及多態的優勢。【藍色字體,點擊進入】學習

【java基礎之多態】理解多態的向上向下轉型從「媽媽我想吃烤山藥」講起開發工具

匿名內部類的使用

開篇已經隱式地提醒過你們了,使用匿名內部類有個前提條件:必須繼承一個父類或實現一個接口,但最多隻能繼承一個父類,或實現一個接口。

關於匿名內部類必需要知道的兩點知識:

一、匿名內部類不能有構造器(構造方法),你想嘛,匿名內部類沒有類名,咋定義構造器,構造方法的方法名是要與類名一致,但匿名內部類能夠定義實例初始化塊。

一、匿名內部類不能夠是抽象類,剛說過了匿名內部類不能有構造器,而抽象類能夠有構造方法,這是其一。java在建立匿名內部類的時候,會當即建立內部類的對象,而抽象類不能建立實例,這是其二。

到這裏,你可能會問,都看不見名字,怎樣判斷一個匿名類的存不存在啊?其實很簡單,咱們講匿名內部類的使用格式以前來看一個小程序,以下

package AnonymousInner;

abstract class Father{
    public abstract void speak();
}

public class NIMingDemo {
    public static void main(String[] args) {
        Father f=new Father();
    }
}

以上這個程序,小白童鞋會不覺得然,而基礎好一點的童鞋就會發現,這是錯誤的寫法。你見過抽象類能夠實例化嘛?顯然不行,會編譯失敗以下: 在這裏插入圖片描述 這個時候,咱們的匿名內部類就登場了~

package AnonymousInner;

abstract class Father{
    public abstract void speak();
}

public class NIMingDemo {
    public static void main(String[] args) {
        Father f=new Father() {
            @Override
            public void speak() {
                System.out.println("劉東強東牆東強");
            }
        };
    }
}

你可能有點濛濛的感受,咋作到滴,先不說怎麼作到代碼實現的,咱們先來看看java開發工具是怎麼實現的,這裏要爲idea點個讚了!idea寫上面的代碼是這樣的 在這裏插入圖片描述eclipse或者Myeclipse工具則是這樣的 在這裏插入圖片描述 經過這裏這個小插件樓主只想告訴各位同窗,idea的喪心病狂的提示絕壁會讓你尖叫的,不只僅是代碼提示仍是編譯運行速度都強於eclipse,因此人生苦短,請直接上手idea,忘了eclipse吧,若是在學ssm框架的同窗能夠參考這篇IDEA優雅整合ssm框架(詳細思路+附帶源碼)

OK,回到正題,咱們把上面的那個程序的靈魂給抽出來,靈魂代碼以下:

abstract class Father(){
....
}
public class NIMingDemo{
   Father f = new Father(){ .... };  //{}裏就是個匿名內部類
}

通常來講,new 一個對象時()後應該是分號;,也就是new出對象該語句就結束了。可是匿名內部類就不同,()後跟的是{}{}中是該new 出對象的具體的實現方法,最後還須要在{}後面加個;表明結束。由於樓主在前面也說過,一個抽象類是不能直接new 的,必須先有實現類了咱們才能new出它的實現類。上面的靈魂代碼就是表示new 的是Father的實現類,只不過這個實現類是個匿名內部類的形式而已。

到這裏咱們已經講了繼承這一方面的例子了,這個時候確定會有小白同窗說剛纔不是一種再講抽象和匿名內部類咩?好像沒有講到繼承,呃呃呃,小白同窗啊抽象類也是一個類,我已是用Son子類繼承了Father父類了,只是這個Father父類就是一個抽象類鴨!!!

至於接口方面也是同樣的,啥?也要講。。。行吧,那下面再來一個匿名內部類用於接口上的例子!

package AnonymousInner;

interface Father{
    public abstract void speak();
}

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

        Father f=new Father() {
            @Override
            public void speak() {
                System.out.println("粑粑:孩子,不你不想拉粑粑");
            }

            public void eatBaBa(){
                System.out.println("熊孩子:粑粑,我想拉粑粑");
            }
        };

        f.speak();
        f.eatBaBa(); //編譯失敗,不能調用eatBaBa()方法
    }
}

寫這個程序的過程當中, f.eatBaBa(); 會編譯失敗,提示不能調用eatBaBa()方法,Why?注意 Father f=new Father()建立的是Father的對象,而非匿名內部類的對象。其實匿名內部類連名字都沒有,你咋實例對象去調用它的方法呢?可是f.speak()方法卻能夠執行,Why?由於匿名內部類實現了接口Fatherspeak()方法,所以能夠藉助Father的對象去調用。

確定會有特別倔強的童鞋就是想調用匿名內部類的自定義的eatBaBa()方法,有沒有辦法呢?固然也是有辦法滴,並且有兩個方法:

方法1、 事實上匿名內部類中隱含一個匿名對象,經過該方法能夠直接調用eatBaBa()speak()方法;具體代碼以下:

package AnonymousInner;

interface Father{
    public abstract void speak();
}

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

        new Father() {
            @Override
            public void speak() {
                System.out.println("粑粑:孩子,不你不想拉粑粑");
            }

            public void eatBaBa(){
                System.out.println("熊孩子:粑粑,我想拉粑粑");
            }
        }.eatBaBa() ;

    }
}
                 
運行結果:
        熊孩子:粑粑,我想拉粑粑

只不過這個匿名對象只能調用一個方法也就是說只能使用一次,我試着調用兩個方法好像行不通,固然大家也能夠嘗試嘗試,博主仍是個菜鳥並不能保證這句話的正確性,若有錯誤請必定要告訴我!感激涕零!!!

方法2、 把eatBaBa()方法更改成speak()方法同樣的使用,也就是說在Father接口中聲明eatBaBa()方法,而後在匿名內部類中覆寫此方法便可。這個就不貼代碼了吧!

匿名方法在多線程上的實現

不得不說匿名方法最經常使用的狀況就是在多線程的實現上,由於要實現多線程必須繼承Thread類或是繼承Runnable接口,開篇的時候寫的就是繼承Runnable接口的代碼,因此這裏博主就講講Thread類的匿名內部類實現,代碼以下:

package AnonymousInner;

public class ThreadDemo {

    public static void main(String[] args) {

        Thread t = new Thread() {
            public void run() {
                for (int i = 1; i <= 3; i++) {
                    System.out.print(i);
                }
            }
        };
        t.start();
    }
}
運行結果:  123

若是本文對你有所幫助,請給博主點一個愛心,支持一下hhhh!!!

結語:博主並不能保證每句話都正確,若有錯誤或者博主理解不夠深入的地方請必定要告訴我!!!

最後,歡迎各位關注個人公衆號,一塊兒探討技術,嚮往技術,追求技術,說好了來了就是盆友喔... 在這裏插入圖片描述

相關文章
相關標籤/搜索