巧妙的理解掌握Java內部類
01 前言
昨天晚上,我把車停好以後就回家了。回家後才發現手機落在車裡面了,但外面太冷,冷到骨頭都能感受到寒意——實在是不想返回一趟去取了(小區的安保還不錯,不用擔心被砸車玻璃),於是打定主意過幾個小時的“世外桃源”生活——別人找不到我,我也找不到別人,這種與世隔絕的狀態非常適合讀書寫作。
把厚厚的《Java程式設計思想》擺在桌子上,正襟危坐,認認真真地讀起了第十章——內部類。儘管我已經非常耐心和用心了,但內部類的這一章非常的枯燥,並且難以理解,我整個人幾乎處於崩潰的邊緣。
很早之前,有想要轉行學習Java的朋友諮詢我,有哪方面的書可以推薦,我鄭重其事地介紹了《Java程式設計思想》,並且一再叮囑他這是一本Java入門級的經典書,必須耐著性子讀完它。現在想想,自己當時的推薦真是輕率!
我這樣說,並不是為了否認《Java程式設計思想》這本書的價值,因為站在書本的角度,它可能會感慨說:這王二的學習能力有問題啊,讀我竟然這麼困難!
不是有那樣一句話嘛:“如果你手裡有一把錘子,所有東西看上去都像釘子。”我認為“內部類”這一章很難懂,其根本的原因在於我對“內部類”沒有很好的理解。想要繼續紮實Java的基礎知識,唯一要做的就是——想盡一切辦法搞懂“內部類”,並梳理成文。
02 內部類的定義
顧名思義,內部類就是放在另外一個類的內部定義的類。非常重要的一點是,內部類能夠訪問外部類的所有成員,包括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要表達什麼意思呢?
答案是:我,沉默王二,已經29歲了,89年出生(有人說89年出生明明是30歲)。上了年紀了,總想裝點嫩,理解一下。我讀書不多,但特別愛思考,於是我就給自己建立了一個會思考的內部類Thought。
答從程式清單1-1可以看得出,儘管Thought是內部類,但可以訪問外部類Wanger的私有成員變數age。
答如果想建立內部類的物件,需要先指明物件引用的型別,格式為OuterClassName.InnerClassName,就像main()方法中的Wanger.Thought那樣。
答緊接著,就要來建立內部類物件了,有兩種形式。第一種形式是先在外部類中定義一個方法Thought getThought(),返回使用new關鍵字建立的內部類物件,然後使用外部類物件呼叫該方法wanger.getThought();第二種形式是直接通過外部類物件.new建立wanger.new Thought()。
03 匿名內部類
答以我的程式設計經驗來看,匿名內部類使用最頻繁的場合就是在建立執行緒的時候。
來看程式清單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的。”
04 為什麼需要內部類
Java的內部類讓我很容易的想起來JavaScript的閉包,閉包就是定義在一個函式內部的函式——這聽起來和Java的內部類定義一樣一樣的。本質上,閉包是將函式內部與函式外部連線起來的橋樑。內部類一樣,它是將內部類與外部類連線起來的橋樑。
來看看什麼是閉包吧:
function wanger() {
var age = 30;
function know() {
console.log(age);
}
}wanger();
// 控制檯輸出30
除此之外,內部類最引人注意的原因是:
內部類可以獨立地繼承一個抽象類或者實現一個介面,無論外部類是否也這樣做了,對內部類都沒有影響。
總而言之,內部類讓類的內容豐富多彩,就好像一個人的心中還可以住著另外一個人。
文章來自: