Java內部類

Java內部類真的很難理解,但有必要搞懂,由於內部類讓外部類更豐富多彩了,就好像一我的的心中還能夠住着另一我的。javascript

0一、前言

昨天晚上,我把車停好之後就回家了。回家後才發現手機落在車裏面了,但外面太冷,冷到骨頭都能感覺到寒意——實在是不想返回一趟去取了(小區的安保還不錯,不用擔憂被砸車玻璃),因而打定主意過幾個小時的「世外桃源」生活——別人找不到我,我也找不到別人,這種與世隔絕的狀態很是適合讀書寫做。html

把厚厚的《Java編程思想》擺在桌子上,正襟危坐,認認真真地讀起了第十章——內部類。儘管我已經很是耐心和用心了,但內部類的這一章很是的枯燥,而且難以理解,我整我的幾乎處於崩潰的邊緣。java

很早以前,有想要轉行學習Java的朋友諮詢我,有哪方面的書能夠推薦,我鄭重其事地介紹了《Java編程思想》,而且一再叮囑他這是一本Java入門級的經典書,必須耐着性子讀完它。如今想一想,本身當時的推薦真是輕率!編程

我這樣說,並非爲了否定《Java編程思想》這本書的價值,由於站在書本的角度,它可能會感慨說:這王二的學習能力有問題啊,讀我居然這麼困難!閉包

不是有那樣一句話嘛:「若是你手裏有一把錘子,全部東西看上去都像釘子。」我認爲「內部類」這一章很難懂,其根本的緣由在於我對「內部類」沒有很好的理解。想要繼續紮實Java的基礎知識,惟一要作的就是——想盡一切辦法搞懂「內部類」,並梳理成文。ide

0二、內部類的定義

顧名思義,內部類就是放在另一個類的內部定義的類。很是重要的一點是,內部類可以訪問外部類的全部成員,包括private修飾的。函數

來看程序清單1-1:學習

public class Wanger {
    private int age;
    public Wanger(int age) {
        this.age = age;
    }

    class Thought {
        public void know() {
            System.out.println("沉默王二的年齡" + age);
        }
    }

    public Thought getThought() {
        return new Thought();
    }

    public static void main(String[] args) {
        Wanger wanger = new Wanger(29);
        Wanger.Thought thought = wanger.getThought();
        thought.know(); // 輸出:沉默王二的年齡29

        // 使用.new的形式建立內部類對象
        Wanger.Thought thought1 = wanger.new Thought();
        thought1.know();
    }
}

程序清單1-1要表達什麼意思呢?ui

答案是:我,沉默王二,已經29歲了,89年出生(有人說89年出生明明是30歲)。上了年紀了,總想裝點嫩,理解一下。我讀書很少,但特別愛思考,因而我就給本身建立了一個會思考的內部類Thought。this

從程序清單1-1能夠看得出,儘管Thought是內部類,但能夠訪問外部類Wanger的私有成員變量age。

若是想建立內部類的對象,須要先指明對象引用的類型,格式爲 OuterClassName.InnerClassName,就像main()方法中的Wanger.Thought那樣。

緊接着,就要來建立內部類對象了,有兩種形式。第一種形式是先在外部類中定義一個方法Thought getThought(),返回使用new關鍵字建立的內部類對象,而後使用外部類對象調用該方法wanger.getThought();第二種形式是直接經過外部類對象.new建立wanger.new Thought()

0三、匿名內部類

以個人編程經驗來看,匿名內部類使用最頻繁的場合就是在建立線程的時候。

來看程序清單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的。」

0四、爲何須要內部類

Java的內部類讓我很容易的想起來JavaScript的閉包,閉包就是定義在一個函數內部的函數——這聽起來和Java的內部類定義同樣同樣的。本質上,閉包是將函數內部與函數外部鏈接起來的橋樑。內部類同樣,它是將內部類與外部類鏈接起來的橋樑。

來看看什麼是閉包吧:

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

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

除此以外,內部類最引人注意的緣由是:

內部類能夠獨立地繼承一個抽象類或者實現一個接口,不管外部類是否也這樣作了,對內部類都沒有影響


上一篇:Java代碼複用的三種經常使用方式:繼承、組合和代理

下一篇:Java String,看這篇就夠了

相關文章
相關標籤/搜索