概述
最近學習python,發現python是支持多繼承的,這讓我想起Java是通過內部類實現的這套機制。這篇文章不是講如何通過內部類實現多繼承,而是總結一下內部類的類型和使用方法。
Java內部類分為:
- 非靜態內部類
- 靜態內部類
- 局部內部類
- 匿名內部類
內部類在Android源碼中被大量的使用,先介紹一下這四種內部類的共同點:
- 內部類仍然是一個獨立的類,在編譯之后內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號。
- 內部類不能用普通的方式訪問。內部類是外部類的一個成員,因為內部類可以自由的訪問外部類的成員,包括private成員。
- 內部類聲明為靜態的,就不能隨意的訪問外部類的成員變量了,此時內部類只能訪問到外部類的靜態成員變量。
接下來,分別介紹一下這幾種內部類。
非靜態內部類
當一個類作為另一個類的非靜態成員時,則這個類就是一個非靜態內部類。
創建非靜態內部類的示例代碼如下:
1
2
3
|
class OutClass { class InnerClass {} } |
當我們用javac去編譯的時候,發現生成了兩個.class文件:OutClass.class和OutClass$InnerClass.class。如下圖所示:
從外部類的非靜態方法中實例化內部類
在外部類中訪問內部類是很容易的,直接創建內部類對象,然后通過對象實例調用類內的方法即可。示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class OutClass { private static int a = 0 ; public void makeInner() { InnerClass inClass = new InnerClass(); inClass.seeOuter(); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.makeInner(); } class InnerClass { public void seeOuter() { System.out.println(a); a++; } } } |
1
|
0 |
從外部類的靜態方法中實例化內部類
在外部類中訪問內部類是比較簡單的,可以直接new出內部類對象,但是如果想在外部類的外部使用內部類,接不能直接new內部類名的方式了,而是需要如下方式:
1
|
OutClass.InnerClass innerClass = new OutClass(). new InnerClass(); |
也就是說,在外部調用非靜態內部類,需要先實例化外部類,然后通過外部類對象再去實例化內部類。示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class OutClass { private static int a = 0 ; public void makeInner() { InnerClass inClass = new InnerClass(); inClass.seeOuter(); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.makeInner(); OutClass.InnerClass innerClass = new OutClass(). new InnerClass(); innerClass.seeOuter(); } class InnerClass { public void seeOuter() { System.out.println(a); a++; } } } |
運行結果:
1
2
|
0 1 |
內部類的this引用
普通的類可以使用this引用當前的對象,內部類也是如此。但是假若內部類想引用外部類當前的對象呢?可以使用如下方式:
外部類名.this
示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class OutClass { private static int a = 0 ; public void makeInner() { InnerClass inClass = new InnerClass(); inClass.seeOuter(); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.makeInner(); OutClass.InnerClass innerClass = new OutClass(). new InnerClass(); innerClass.seeOuter(); } class InnerClass { public void seeOuter() { System.out.println( this ); System.out.println(OutClass. this ); } } } |
靜態內部類
上面介紹了非靜態內部類,接下來我們學習神馬是靜態內部類。
靜態內部類就是在外部類中扮演一個靜態成員的角色,創建靜態內部類和創建非靜態內部類的形式很相似,只是class前面多了一個static修飾符。
注意,外部類是不可能使用static修飾符進行修飾的。
示例代碼如下:
1
2
3
4
|
class OutClass { static class InnerClass { } } |
用javac命令編譯一下,可以看到一樣都是有兩個.class文件,如下圖所示:
從外部類的非靜態方法中實例化靜態內部類
從外部類中訪問靜態內部類,和在外部類中訪問非靜態內部類是一樣的。但是,需要注意一點,此時靜態內部類只能訪問外部類的靜態成員,無法訪問非靜態成員了。
示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class OutClass { private static int a = 0 ; private int b = 1 ; public void makeInner() { InnerClass inClass = new InnerClass(); inClass.seeOuter(); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.makeInner(); } static class InnerClass { public void seeOuter() { System.out.println( this ); System.out.println(a); // System.out.println(b); } } } |
執行結果如下:
1
2
|
OutClass$InnerClass@79a340 0 |
從外部類靜態方法中實例化靜態內部類
注意:
因為靜態內部類是外部類的靜態成員,而靜態成員是跟類綁定,而不是跟類實例化的對象綁定。所以,在外部類的靜態方法中實例化內部類,是不需要先實例化外部類的。
示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class OutClass { private static int a = 0 ; private int b = 1 ; public void makeInner() { InnerClass inClass = new InnerClass(); inClass.seeOuter(); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.makeInner(); OutClass.InnerClass inClass = new OutClass.InnerClass(); inClass.seeOuter(); } static class InnerClass { public void seeOuter() { System.out.println( this ); System.out.println(a); // System.out.println(b); } } } |
匿名內部類
匿名內部類在Android應用開發中簡直是泛濫,各種listener對象的實現很多都是通過匿名內部類。
匿名內部類從名字上就可以知道這是代表沒有類名的內部類,通常用來簡化代碼。
相信寫Java的同學都使用過線程,那Thread的時候我們可以傳一個Runnable對象,也可以傳一個匿名內部類。示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class OutClass { public void testAnonymousClass() { Thread t = new Thread( new Runnable() { @Override public void run() { for ( int i = 0 ; i < 10 ; i ++) { System.out.println(i); try { Thread.sleep( 500 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }); t.start(); System.out.println( "another thread is running..." ); } public static void main(String[] args) { OutClass oClass = new OutClass(); oClass.testAnonymousClass(); } } |
執行結果如下:
1
|
another thread is running... |