一:内部類
1:什麼是内部類?
大部分時候,類被定義成一個獨立的程式單元。在某些情況下,也會把一個類放在另一個類的内部定義,這個定義在其他類内部的類就被稱為内部類(有些地方也叫做嵌套類),包含内部類的類也被稱為外部類(有些地方也叫做宿主類)
我們先建立一個基本的内部類結構:
class Outer{//外部類
//内部類
class Inner{
}
}
2:内部類的劃分
内部類分為成員内部類和局部内部類。内部類也會生成.class檔案。

2.1: 成員内部類
定義在外部類中的成員位置,與類中的成員變量相似,可通過外部類對象進行通路。
内部類可以使用外部類的成員,包括私有成員。但是外部類要使用内部類的成員,必須建立内部類變量。
2.2: 局部内部類(比較少用)
定義:在方法裡面有一個内部類。
隻有在内部類所屬的方法中建立内部類對象,方可通路局部内部類。而測試類中隻需要建立外部類對象,然後調用外部類方法即可。
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
除此之外,内部類最引人注意的原因是:
内部類可以獨立地繼承一個抽象類或者實作一個接口,無論外部類是否也這樣做了,對内部類都沒有影響。