JAVA 類總結 最近看了遍java內部類相關的一些內容,做一些總結。與個人博客 zhiheng.me 同步發佈,標題: JAVA 類總結。 頂級類與嵌套類 定義在某個類(或介面,下同)內部的類,稱為嵌套類(nested class),相應的,其所在的類稱之為該類的外圍類(enclosing cla ...
JAVA 類總結
最近看了遍java內部類相關的一些內容,做一些總結。與個人博客 zhiheng.me 同步發佈,標題: JAVA 類總結。
頂級類與嵌套類
定義在某個類(或介面,下同)內部的類,稱為嵌套類(nested class),相應的,其所在的類稱之為該類的外圍類(enclosing class)或包裹類。
非嵌套類稱為頂級類(top-level class),一個 .java 文件中可以有若幹個頂級類(含抽象類和介面),但只能有一個被 public 修飾的類,且該類必須和 .java 文件同名。
頂級類的訪問修飾符只能是 public 和包訪問許可權(預設許可權,無修飾符)。
嵌套類可看作是外圍類的一個成員,因此其修飾符可以是 public 、protected 、包訪問許可權和 private 。
嵌套類沒有層次限制,可以在嵌套類裡面在定義類,成為嵌套類中的嵌套類。
嵌套類分為兩種,一種是靜態的(用 static 關鍵字修飾)稱為靜態嵌套類(static nested class);一種是非靜態的,稱為內部類(inner class)。
註:在《Think in Java》一書中,作者將內部類定義為“將一個類定義在另一個類的定義內部,則這個類就是內部類”,因此,他將靜態嵌套類視為內部類的一種。而本文使用了 java 官方文檔中的定義。
內部類一般直接定義在外部類(outer class)中,就像該類的一個成員一樣,我們把這樣的內部類稱為成員內部類(member inner class)。即不在構造器、方法、語句塊中定義的內部類為成員內部類。
除成員內部類外還有另外兩種較特殊的內部類:局部內部類(local class)和匿名內部類(anonymous class)。
嵌套類位元組碼文件命名
嵌套類經編譯後會自動生成獨立的位元組碼文件(.class),其命名格式:
外部類名稱+\$+[該種類同名類中該類順序]+[內部類名稱]
以下代碼(文件名:Outer.java)中含有靜態類 Static 、成員內部類 Inner 、局部類 Local 、實現 Anonymous 介面的匿名類以及定義在該源文件中的介面 Anonymous 。
package thinkinjava; public class Outer { public static class Static{} public class Inner {} { class Local{}; } Anonymous anonymous = new Anonymous(){}; } interface Anonymous {}
編譯後形成瞭如下6個 .class 位元組碼文件。頂級類 Outer 和 Anonymous 都被編譯成同名的 class 文件,靜態嵌套類 Static 和成員內部類 Inner 被編譯成了 Outer\$Static.class 和 Outer\$Inner.class ,因為成員類不能同名,所以也就沒有同名類順序。局部類 Local 編譯後的文件名是 Outer\$1Local.class ,因為 Outer 類中只有一個名為 Local 的局部類,因此,其順序是1。匿名類沒有名稱,所以編譯後的文件名是 Outer\$1.class ,1表示該類是 Outer 類中第一個匿名類。
Anonymous.class Outer$1.class Outer$1Local.class Outer$Inner.class Outer$Static.class Outer.class
靜態嵌套類
public class OuterClass{ public static class NestClass{} }
靜態嵌套類因為是靜態的,因此從本質上來說它和外部類的關係更像是類與包(package)的關係。在其他類中引用使用的時候需要加上外部類限定: OuterClass.NestClass
。
- 與靜態方法一樣,靜態嵌套類中不能訪問外部類的非靜態成員和非靜態方法(不管是public還是private的);
- 靜態嵌套類的實例化(instantiate)無需事先實例化外部類,因為靜態嵌套類是與外部類直接相關聯的,而非與外部類的實例(instance)相關聯。
內部類
內部類是非靜態的,因此內部類是與外部類的實例相關聯的。在實例化內部類時,必須先行實例化外部類,再通過外部類的實例來創建內部類的實例:
OuterClass outObject = new OuterClass(); OuterClass.InnerClass innerObject = outerObject.new InnerClass();
- 內部類中不能有 static 關鍵字修飾的靜態成員(塊、欄位、方法、介面等),除非該成員是靜態常量。所以,內部類中的靜態成員必須是同時使用 final 關鍵字修飾的欄位。
- 內部類可以訪問外部類的任何成員(包括構造器),不管是公有的還是私有的,靜態的還是非靜態的。同樣,外部類也可以訪問到內部類的所有成員。
遮蔽(Shadowing)
定義在內部類或成員方法內的欄位或參數,如果和外部作用域內的某個成員變數定義同名,那麼外部的定義將被遮蔽,此時無法在內部作用域內僅通過名字訪問到外部的成員。以下是摘自 Java Tutorial 中的一個例子:
public class ShadowTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { System.out.println("x = " + x); System.out.println("this.x = " + this.x); System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); } } public static void main(String... args) { ShadowTest st = new ShadowTest(); ShadowTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); } } // 輸出如下 // x = 23 // this.x = 1 // ShadowTest.this.x = 0
局部內部類
局部類是定義在某個塊(block)中的類。即定義在構造器、方法、迴圈體、分支結構(if 子句)中的類。
- 同局部變數,局部類不能用public,private,protected,static修飾,但可以被final或者abstract修飾。
- 局部類是內部類,因此可以訪問其外部類的成員。但局部類的作用域在塊內,所以外部類無法訪問到局部內部類。
局部類屬於塊的作用域,因此可以訪問局部變數(包括形參),但是只能訪問用 final 修飾的局部變數。
在 Java SE 8 之後,局部類可以訪問 effectively final 的局部變數和非 final 的形參了,effectively final 的變數沒有 final 修飾但在初始化後從未改變過值。 “A variable or parameter whose value is never changed after it is initialized is effectively final” 。
匿名類
匿名類,顧名思義就是沒有名稱的類,沒有名稱也就無法在其他地方引用和實例化,當然也就沒有構造器。匿名類在定義的同時會實例化本身(匿名類只實例化這一次)。
匿名類的定義從形式上看更像是一種表達式,也就是類的定義出現在一個表達式中。從語法形式上看,匿名類的定義像是調用了一個構造器。以下是幾種匿名類的例子:
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一個類實例。