[轉載] Java各種稱呼類詳解
Java有各種各樣類,內部類、巢狀類、成員類、區域性類(本地類)、靜態類、匿名類、檔案類以及這些組合起來的稱呼類,成員內部類,成員匿名類,成員巢狀類,本地匿名類等,真是多的不行,但有些其實是一個意思,在這裡好好理一理。
宣告
1.注意,這些稱呼都是翻譯過來的,但是同一個英文單詞或一個片語翻譯過來可能有不同叫法,比如local nested class,local有區域性、本地意思,其實是一個意思,local nested class翻譯過來就是區域性巢狀類(本地巢狀類),又因為非靜態巢狀類都稱為內部類,所以local nested class又叫為內部區域性類(本地內部類),最終又簡稱為:區域性類(本地類)。我個人覺得區域性類更加形象點所以下面都優先採用區域性類一說。
2.我通過搜尋找到兩種不太相同的內部類定義,一種認為:定義在類裡面的稱為巢狀類(nested class)
但是對於大多數中國程式設計師,貌似將定義在類裡面的類稱為內部類不是更符合文意麼?也許這就是為什麼會有這麼多人一直認為靜態內部類的存在。嘛,反正不管怎麼稱呼,只要知道代表什麼意思就好,細節什麼的不要在意(光哥:所以不注意細節的你寫的程式碼才這麼一大堆bug麼?還不趕緊去改(怒吼))。
我這裡採用Java語言規範上說的。
0.檔案類
檔案類放在最前面,是因為檔案類是與主類(一個檔案中public類)關係最不密切的一類。什麼是檔案類?看程式碼就知道:
public class Main{}
class Test{}//Test就是檔案類
//是的,一個.java檔案裡面定義在主類外面的就是檔案類
//主類、檔案類稱為頂級類(top level class),Java語言規範中定義:非巢狀類即為頂級類。
- 1
- 2
- 3
- 4
【注意】:主類這一定義是我自己按語義稱呼的,有的地方稱為基本類,但我覺得很不符合語義,Java語言規範我也沒找到相關定義。
- 因為一個.java檔案只能有一個主類(public 類),所以檔案類預設只能是包訪問許可權,即:不是同一個包的是無法引入和使用的。
1.巢狀類
由上面檔案類可有類似定義:一個.java檔案裡面定義在類裡面的就是巢狀類,定義在類內部,包括塊、靜態塊、構造器、方法內。這個時候該類也相對來被稱為包裝類(enclosing class)或外部類。
巢狀類是可以有層次的,也就是說巢狀類裡面還可以定義類,成為巢狀類中的巢狀類。
在Java語言規範裡面,巢狀類定義是:
A nested classisanyclasswhosedeclarationoccurs within thebodyof
another classorinterface.
- 1
- 2
說的簡單一點,就是定義在類(這裡還包括介面,下同)裡面的類。所以說,以下所有的類都可以稱為巢狀類。巢狀類分為兩種:靜態巢狀類和非靜態巢狀類,非靜態巢狀類就是內部類(inner class)。
靜態巢狀類
簡稱靜態類,和主類關係也不大,只是在其他類中引用使用的時候需要加上外部類限定,但在技術上來看,完全是兩個獨立無關係的類。
public class Main{
public static class NestClass{}
}
//在外面使用時候形式如下,在Main中使用則不需要加上外部類限定
Main.NestClass nestClass = new Main.NestClass();
- 1
- 2
- 3
- 4
- 5
從形式上來看,靜態類可以說是類的一個靜態成員(所以也可以說是成員類一種),但從技術上來看,其實二者沒什麼關係,可以看做第三類頂級類。但也因此,靜態類不怎麼常用,因為它同一般類沒什麼優勢可言。
- 靜態類不能訪問外部類的非靜態成員和非靜態方法(不管是public還是private的);
- 靜態類的例項不需要先例項化外部類成員,可直接例項化。
2.內部類
Java語言規範裡的定義:
An inner class is a nested class that is not explicitly or implicitly
declared static.
//即非靜態巢狀類即為內部類
- 1
- 2
- 3
內部類包括:成員類、區域性類、匿名類。
- 內部類中不能有靜態修飾的成員(比如塊、欄位、方法、介面等),總之不能有static關鍵字,除了一種情況,那就是靜態常量,又因為常量成員欄位必須在宣告的時候初始化,所以形式只能如:
public static final int a = 6;
- 內部類可以訪問外部類任何成員,不管是公有的還是私有的,靜態的還是非靜態的(並且內部類的成員的名字也可以同外部相同,只不過這樣會覆蓋掉去外部類的),這是因為每一個內部類都儲存了一個對外部類的一個引用。這很好理解,因為你要例項化這個內部類,肯定是通過外部類的一個例項,而內部類保留的這個引用就是這個外部類例項。
- 內部類命名格式:外部類名稱++[該種類同名類中該類順序]+[內部類名稱],例如成員類,成員類不能同名,所以也就沒有同名類順序:com.fcc.test.OuterClassMemberClass;區域性類:com.fcc.test.OuterClass1LocalClass;匿名類:匿名類沒有名稱,所以格式如:com.fcc.OuterClass1。
成員類
這裡說的是非靜態成員內部類(如果靜態巢狀類也算是成員類一種的話),non-static member (inner) class。而一般說的也是這種,但從技術上來看,成員類應該還包括靜態巢狀類。
A member class is a class whose declaration is directly enclosed in
another class or interface declaration.
- 1
- 2
成員類算是最常見最常用的一種內部類,我們一般說的內部類說的就是成員類:在類裡面,但不在塊、構造器、方法裡面。
//成員類,從技術上來說,可以分為兩種:成員內部類和成員巢狀類。
//1.成員內部類即這裡說的成員類,全稱是非靜態成員內部類
//2.成員巢狀類即上面的靜態巢狀類
public class Main{
public class MemberClass{}//成員內部類,常簡稱為成員類
}
- 1
- 2
- 3
- 4
- 5
- 6
成員類,可以使用public,private,protected訪問控制符,也可以用static,final關鍵字修飾,並且有enclose class屬性。
這裡題外說明一下:
- 成員(member),只要是在類裡面的(但不在塊、構造器、方法內),都是成員:可以是變數,就是成員變數(一般又稱為成員欄位,Field);可以是方法,好吧,方法都是成員(因為Java中方法不可能位於類外)的;當然,同樣的,也可以是介面、列舉、註釋類以及類。
- 關於static理解,有static修飾的是類本身屬性(共性),所以訪問可以不通過類的例項物件,而非static修飾的,是物件屬性(個性),必須通過類的例項物件訪問,因為個性是個體的屬性啊,當然要創建出個體,然後這個個性才有意義。
- 關於enclose class,enclose method,enclose constructor屬性,可以理解為這個類是被類、還是方法、構造器包裝起來的。關於這些屬性,可以參考Class類:Java原始碼解析(2) —— Class(1)。
區域性類(本地類)
local nested class,區域性巢狀類,簡稱區域性類,區域性類所屬範圍:在塊、構造器以及方法內,這裡的塊包括普通塊和靜態塊。區域性類只在本塊範圍內有效。
定義:
A local class is a nested class (§8) that is not a member of any class
and that has a name.
- 1
- 2
翻譯過來就是:區域性類是巢狀類,但不是成員類,而且有名稱(不是匿名類)。
public class Test {
{
class AA{}//塊內區域性類
}
public Test(){
class AA{}//構造器內區域性類
}
public static void main(String[] args){
}
public void test(){
class AA{}//方法內區域性類
}
}
//注意到了吧,可以同名,編譯後,形成諸如:外部類名稱+$+同名順序+區域性類名稱
//Test$1AA.class/Test$2AA.class/Test$3AA.class
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
區域性類最多隻能有final修飾,但不同的是,塊內區域性類有enclose class屬性,而構造器區域性類有enclose constructor屬性,方法區域性類有enclose method屬性,嘛,其實很好理解的吧,一看就知道。
- 區域性類只能訪問(使用)這個塊中(區域性類外)final屬性。這裡的塊包括了上面說的塊、構造器、方法。
匿名類
定義:
An anonymous class declaration is automatically derived from a class
instance creation expression by the Java compiler
- 1
- 2
匿名類,就是沒有名稱的類,其名稱由Java編譯器給出,一般是形如:外部類名稱+$+匿名類順序,沒有名稱也就是其他地方就不能引用,不能例項化,只用一次,當然也就不能有構造器。
匿名類根據位於地方不同分為:成員匿名類和區域性匿名類。
public class Test {
InterfaceA a = new InterfaceA() {};//成員匿名類
public static void main(String[] args){
InterfaceA a = new InterfaceA() {};//區域性匿名類
//以上兩種是通過實現介面實現匿名類,稱為介面式匿名類,也可以通過繼承類
Test test = new Test(){};//繼承式匿名類
//還可以是位於引數上
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();//屬於區域性匿名類一種
}
private interface InterfaceA{}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
匿名類不能使用任何關鍵字和訪問控制符,匿名類和區域性類訪問規則一樣,只不過內部類顯式的定義了一個類,然後通過new的方式建立這個區域性類例項,而匿名類直接new一個類例項,沒有定義這個類。匿名類最常見的方式就是回撥模式的使用,通過預設實現一個介面建立一個匿名類然後,然後new這個匿名類的例項。
總結
本文討論的是:巢狀類、內部類、成員類、區域性類、匿名類相關,這些類的劃分主要是根據類宣告(或位於)的地方劃分的:
1.巢狀類,位於類內部,又分為:靜態巢狀類和非靜態巢狀類。
2.靜態巢狀類即為靜態類。靜態類只有這一種,技術來說,也可以看成靜態成員類。
3.非靜態巢狀類即為內部類,又分為:成員類、區域性類(本地類)、匿名類。
4.成員類:位於類內部但不包括位於塊、構造器、方法內,且有名稱的類。
5.區域性類:位於塊、構造器、方法內的有名稱類。
6.匿名類:類內無名稱類,又可細分為:成員匿名類和區域性匿名類。