1. 程式人生 > >Java 內部類、成員類、區域性類、匿名類等

Java 內部類、成員類、區域性類、匿名類等

Java各種稱呼類詳解

  Java有各種各樣類,內部類、巢狀類、成員類、區域性類(本地類)、靜態類、匿名類、檔案類以及這些組合起來的稱呼類,成員內部類,成員匿名類,成員巢狀類,本地匿名類等,真是多的不行,但有些其實是一個意思,在這裡好好理一理。

宣告

  1.注意,這些稱呼都是翻譯過來的,但是同一個英文單詞或一個片語翻譯過來可能有不同叫法,比如local nested class,local有區域性、本地意思,其實是一個意思,local nested class翻譯過來就是區域性巢狀類(本地巢狀類),又因為非靜態巢狀類都稱為內部類,所以local nested class又叫為內部區域性類(本地內部類),最終又簡稱為:區域性類(本地類)。我個人覺得區域性類更加形象點所以下面都優先採用區域性類一說。
  2.我通過搜尋找到兩種不太相同的內部類定義,一種認為:定義在類裡面的稱為巢狀類(nested class)

,而非靜態巢狀類才是內部類(inner class),也就是說巢狀類包含內部類;另外一種則是認為:定義在類裡面的稱為內部類,這樣靜態巢狀類也是內部類,也就是說巢狀類=內部類。但是,第一種才是Java語言規範規定的,我一開始不確定,找到Java7的語言規範驗證,結果確實如此,即第一種才是正確的,那麼問題來了:依照第一種劃分,靜態內部類其實是一種錯誤說法。。。只有靜態巢狀類,內部類是沒有靜態一說的!
  但是對於大多數中國程式設計師,貌似將定義在類裡面的類稱為內部類不是更符合文意麼?也許這就是為什麼會有這麼多人一直認為靜態內部類的存在。嘛,反正不管怎麼稱呼,只要知道代表什麼意思就好,細節什麼的不要在意(光哥:所以不注意細節的你寫的程式碼才這麼一大堆bug麼?還不趕緊去改(怒吼))。
  我這裡採用Java語言規範上說的。

0.檔案類

  檔案類放在最前面,是因為檔案類是與主類(一個檔案中public類)關係最不密切的一類。什麼是檔案類?看程式碼就知道:

public class Main{}
class Test{}//Test就是檔案類
//是的,一個.java檔案裡面定義在主類外面的就是檔案類
//主類、檔案類稱為頂級類(top level class),Java語言規範中定義:非巢狀類即為頂級類。

【注意】:主類這一定義是我自己按語義稱呼的,有的地方稱為基本類,但我覺得很不符合語義,Java語言規範我也沒找到相關定義。

  1. 因為一個.java檔案只能有一個主類(public 類),所以檔案類預設只能是包訪問許可權,即:不是同一個包的是無法引入和使用的。

1.巢狀類

  由上面檔案類可有類似定義:一個.java檔案裡面定義在類裡面的就是巢狀類,定義在類內部,包括塊、靜態塊、構造器、方法內。這個時候該類也相對來被稱為包裝類(enclosing class)或外部類。
  巢狀類是可以有層次的,也就是說巢狀類裡面還可以定義類,成為巢狀類中的巢狀類。
  在Java語言規範裡面,巢狀類定義是:

A nested class is any class whose declaration occurs within the body of 
another class or interface. 

  說的簡單一點,就是定義在類(這裡還包括介面,下同)裡面的類。所以說,以下所有的類都可以稱為巢狀類。巢狀類分為兩種:靜態巢狀類和非靜態巢狀類,非靜態巢狀類就是內部類(inner class)。

靜態巢狀類

  簡稱靜態類,和主類關係也不大,只是在其他類中引用使用的時候需要加上外部類限定,但在技術上來看,完全是兩個獨立無關係的類。

public class Main{
    public static class NestClass{}
}
//在外面使用時候形式如下,在Main中使用則不需要加上外部類限定
Main.NestClass nestClass = new Main.NestClass();

  從形式上來看,靜態類可以說是類的一個靜態成員(所以也可以說是成員類一種),但從技術上來看,其實二者沒什麼關係,可以看做第三類頂級類。但也因此,靜態類不怎麼常用,因為它同一般類沒什麼優勢可言。

  1. 靜態類不能訪問外部類的非靜態成員和非靜態方法(不管是public還是private的);
  2. 靜態類的例項不需要先例項化外部類成員,可直接例項化。

2.內部類

  Java語言規範裡的定義:

An inner class is a nested class that is not explicitly or implicitly 
declared static.
//即非靜態巢狀類即為內部類

內部類包括:成員類、區域性類、匿名類。

  1. 內部類中不能有靜態修飾的成員(比如塊、欄位、方法、介面等),總之不能有static關鍵字,除了一種情況,那就是靜態常量,又因為常量成員欄位必須在宣告的時候初始化,所以形式只能如:public static final int a = 6;
  2. 內部類可以訪問外部類任何成員,不管是公有的還是私有的,靜態的還是非靜態的(並且內部類的成員的名字也可以同外部相同,只不過這樣會覆蓋掉去外部類的),這是因為每一個內部類都儲存了一個對外部類的一個引用。這很好理解,因為你要例項化這個內部類,肯定是通過外部類的一個例項,而內部類保留的這個引用就是這個外部類例項。
  3. 內部類命名格式:外部類名稱+$+[該種類同名類中該類順序]+[內部類名稱],例如成員類,成員類不能同名,所以也就沒有同名類順序:com.fcc.test.OuterClass$MemberClass;區域性類:com.fcc.test.OuterClass$1LocalClass;匿名類:匿名類沒有名稱,所以格式如:com.fcc.OuterClass$1。

成員類

  這裡說的是非靜態成員內部類(如果靜態巢狀類也算是成員類一種的話),non-static member (inner) class。而一般說的也是這種,但從技術上來看,成員類應該還包括靜態巢狀類。

A member class is a class whose declaration is directly enclosed in 
another class or interface declaration.

  成員類算是最常見最常用的一種內部類,我們一般說的內部類說的就是成員類:在類裡面,但不在塊、構造器、方法裡面。

//成員類,從技術上來說,可以分為兩種:成員內部類和成員巢狀類。
//1.成員內部類即這裡說的成員類,全稱是非靜態成員內部類
//2.成員巢狀類即上面的靜態巢狀類
public class Main{
    public class MemberClass{}//成員內部類,常簡稱為成員類
}

  成員類,可以使用public,private,protected訪問控制符,也可以用static,final關鍵字修飾,並且有enclose class屬性。
這裡題外說明一下:

  1. 成員(member),只要是在類裡面的(但不在塊、構造器、方法內),都是成員:可以是變數,就是成員變數(一般又稱為成員欄位,Field);可以是方法,好吧,方法都是成員(因為Java中方法不可能位於類外)的;當然,同樣的,也可以是介面、列舉、註釋類以及類。
  2. 關於static理解,有static修飾的是類本身屬性(共性),所以訪問可以不通過類的例項物件,而非static修飾的,是物件屬性(個性),必須通過類的例項物件訪問,因為個性是個體的屬性啊,當然要創建出個體,然後這個個性才有意義。
  3. 關於enclose class,enclose method,enclose constructor屬性,可以理解為這個類是被類、還是方法、構造器包裝起來的。關於這些屬性,可以參考Class類:Java原始碼解析(2) —— Class(1)

區域性類(本地類)

  local nested class,區域性巢狀類,簡稱區域性類,區域性類所屬範圍:在塊、構造器以及方法內,這裡的塊包括普通塊和靜態塊。區域性類只在本塊範圍內有效。
定義:

A local class is a nested class8) that is not a member of any class 
and that has a name.

翻譯過來就是:區域性類是巢狀類,但不是成員類,而且有名稱(不是匿名類)。

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

  區域性類最多隻能有final修飾,但不同的是,塊內區域性類有enclose class屬性,而構造器區域性類有enclose constructor屬性,方法區域性類有enclose method屬性,嘛,其實很好理解的吧,一看就知道。

  1. 區域性類只能訪問(使用)這個塊中(區域性類外)final屬性。這裡的塊包括了上面說的塊、構造器、方法。

匿名類

定義:

An anonymous class declaration is automatically derived from a class 
instance creation expression by the Java compiler

  匿名類,就是沒有名稱的類,其名稱由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{}
}

  匿名類不能使用任何關鍵字和訪問控制符,匿名類和區域性類訪問規則一樣,只不過內部類顯式的定義了一個類,然後通過new的方式建立這個區域性類例項,而匿名類直接new一個類例項,沒有定義這個類。匿名類最常見的方式就是回撥模式的使用,通過預設實現一個介面建立一個匿名類然後,然後new這個匿名類的例項。

總結

  本文討論的是:巢狀類、內部類、成員類、區域性類、匿名類相關,這些類的劃分主要是根據類宣告(或位於)的地方劃分的:
1.巢狀類,位於類內部,又分為:靜態巢狀類和非靜態巢狀類。
2.靜態巢狀類即為靜態類。靜態類只有這一種,技術來說,也可以看成靜態成員類。
3.非靜態巢狀類即為內部類,又分為:成員類、區域性類(本地類)、匿名類。
4.成員類:位於類內部但不包括位於塊、構造器、方法內,且有名稱的類。
5.區域性類:位於塊、構造器、方法內的有名稱類。
6.匿名類:類內無名稱類,又可細分為:成員匿名類和區域性匿名類。