Nested Classes定義
在java語言規范里面,嵌套類(Nested Classes)定義是:
A nested class is any class whose declaration occurs within the body of another class or interface. A top level class is a class that is not a nested class.
說的簡單一點,就是定義在類里面的類。一般把定義內部類的外圍類成為包裝類(enclosing class)或者外部類
嵌套類分類
根據nested class定義的地方,可以分為member nested class,local nested class , anonymous nested class
member nested class(成員嵌套類) :成員嵌套類 作為 enclosing class 的成員定義的,成員嵌套類有enclosing class屬性
local nested class (局部嵌套類): 局部嵌套類定義在 enclosing class 的方法里面,局部嵌套類有enclosing class 屬性和enclosing method 屬性
anonymous nested class(匿名嵌套類):匿名嵌套類沒有顯示的定義一個類,直接通過new 的方法創建類的實例。一般回調模式情況下使用的比較多
member nested class 可以使用public,private,protected訪問控制符,也可以用static,final關鍵字
local nested class 可以使用final關鍵字
anonymous nested class 不使用任何關鍵字和訪問控制符
見下面的代碼
public class EnclosingClass { public static final class NestedMemberClass { } public void nestedLocalClass() { final class NestedLocalClass { } } public void nestedAnonymousClass() { new Runnable() { @Override public void run() { } }; } }
在大多數情況下,一般把nested classes 分為兩種:
Static Nested Classes(靜態嵌套類): 就是用static修飾的成員嵌套類
InnerClass:靜態嵌套類之外所有的嵌套類的總稱,也就是沒有用static定義的nested classes,Inner Classes 不能定義為static,不能有static方法和static初始化語句塊。在JLS(java語言規范)里面是這么定義的:
An inner class is a nested class that is not explicitly or implicitly declared static. Inner classes may not declare static initializers (§8.7) or member inter- faces
其中Inner Class又可以分為三種:
1 inner member classes :沒有用static 修飾的成員內部類
2 local inner classes : 定義在方法里面的內部類,方法可以是static的也可以是非static的,也可以是構造器方法。
3 anonymous inner classes :定義在方法里面匿名類,方法可以是static的也可以是非static的
嵌套類訪問規則
Static Nested Classes 以及 inner classes 有一些限制規則,下面介紹一下這些規則。
- Static Nested Classes訪問規則
用Static修飾的Nested Classes,只能訪問外部類的非static變量。對於public 的 static Nested Classes 可以用 new 外部類.內部類()的方式直接創建。而默認的static Nested Classes 可以在同一包名下,用 new 外部類.內部類()的方式創建。其實和外部類的方式差不多。靜態成員類可以使用訪問控制符,可以使用static修飾,可以是abstract抽象類
public class StaticNestedClass { // 私有局部 private int i = 0; // 靜態 public static int j = 0; // 不變值 private final int k = 0; // static final private static final int m = 0; // 靜態嵌套內,這里不是innerclass,可以直接new出來 public static class PublicNestedClass { private void test1() { // System.out.println(i); 非innerClass不能訪問enclosing類的非static屬性 System.out.println(j); System.out.println(m); // System.out.println(k); 非innerClass不能訪問enclosing類的非static屬性 } // 可以定義static方法 private static void test2() { } } // 靜態嵌套內,這里不是innerclass,由於是私有的,不可以直接new出來 private static class PrivateNestedClass { } }
下面的例子演示了static Nested class的創建
public class TestClass { public static void main(String[] args) { //任何地方都可以創建 StaticNestedClass.PublicNestedClass publicNestedClass = new StaticNestedClass.PublicNestedClass(); //可以在同一package下創建 StaticNestedClass.DefaultNestedClass defaultNestedClass = new StaticNestedClass.DefaultNestedClass(); //編譯錯誤,無法訪問內部內 //StaticNestedClass.PrivateNestedClass privateNestedClass = new StaticNestedClass.PrivateNestedClass(); } }
- Inner Class訪問規則
inner member classes(內部成員類) 可以訪問外部類的所有實例屬性,靜態屬性。因為內部成員類持有一個外部對象的引用,內部類的實例可以對外部類的實例屬性進行修改。如果是public的 inner member classes,可以通過 外部類實例.new 內部類()的方式進行創建,當調用內部類的構造器的時候,會把當前創建的內部類對象實例中持有的外部對象引用賦值為當前創建內部類的外部類實例。內部成員類可以是使用訪問控制符,可以定義為final,也可以是抽象類。
public class MemberInnerClass { // 私有局部 public int i = 0; // 靜態 private static int j = 0; // 不變值 private final int k = 0; // static final private static final int m = 0; public class PublicMemberInnerClass { // enclosing Class的屬性都可以訪問 public void test() { System.out.println(i); System.out.println(j); System.out.println(m); System.out.println(k); } public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } // 這里會報錯,不允許定義static方法 // private static final void test(); } // 私有的innerclass 外部不能訪問 private class PrivateMemberInnerClass { } // 公開局部類,外部可以訪問和創建,但是只能通過OutterClass實例創建 class DefaultMemberInnerClass { public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } } }
下面例子演示了內部成員類的創建
public class TestClass { public static void main(String[] args) { // 任何地方都可以創建 MemberInnerClass t = new MemberInnerClass(); // 可以創建,pmic里面保存對t的引用 MemberInnerClass.PublicMemberInnerClass pmic = t.new PublicMemberInnerClass(); // 可以在同一package下創建,dmic保存對t的引用 MemberInnerClass.DefaultMemberInnerClass dmic = t.new DefaultMemberInnerClass(); // 編譯錯誤,無法訪問內部內 // MemberInnerClass.PrivateMemberInnerClass pmic = t.new // PrivateMemberInnerClass(); // 下面驗證一下outterClass是同一個對象 System.out.println(pmic.getOutterClass() == t); System.out.println(dmic.getOutterClass() == t); } }
運行程序,打印結果:
true true
2 local inner classes(局部類)
局部類 定義在類方法里面。這個方法既可以是靜態方法,也可以是實例方法,也可以是構造器方法或者靜態初始化語句塊。
局部類可以定義在一個static上下文里面 和 非static上下文里面。局部類不能有訪問控制符(private,public,protected修飾),可以是抽象的,也可以定義為final
定義在static上下文(static 字段初始化,static初始化塊,static方法)里面的local inner classes 可以訪問類的靜態屬性,如果定義在靜態方法里面的局部類,還可以方法里面定義的final變量。在static上下文定義的局部類,沒有指向父類實例變量的引用,因為static方法不屬於類的實例,屬於類本身。而且局部類不能在外部進行創建,只能在方法調用的時候進行創建
public class LocalInnerClass { // 私有局部 private int i = 0; // 靜態 public static int j = 0; // 不變值 private final int k = 0; // static final private static final int m = 0; public static void test() { final int a = 0; int b = 0; // local inner class不能夠有訪問控制符 比如public private abstract class LocalStaticInnerClass { // local inner class不能定義靜態屬性 // private static int c; private int d = 0; public LocalStaticInnerClass() { // 可以訪問方法里面定義的final 變量 System.out.println(a); // 不能訪問b 因為b不是final // System.out.println(b); // 定義在static上下文里面的local inner class 不能訪問外部類的非static字段 // System.out.println(i); // System.out.println(k); System.out.println(j); System.out.println(m); } // local inner class不能定義靜態方法 // public static void test(){} } } public void test2() { final int a = 0; int b = 0; final class LocalNonStaticInnerClass{ public LocalNonStaticInnerClass() { //定義在非static上下文的local inner class 可以訪問外部類的所有屬性 System.out.println(i); System.out.println(k); System.out.println(j); System.out.println(m); } } } }
3 anonymous inner classes (匿名類)也是定義在方法里面,匿名類和局部類訪問規則一樣,只不過內部類顯式的定義了一個類,然后通過new的方式創建這個局部類實例,而匿名類直接new一個類實例,沒有定義這個類。匿名類最常見的方式就是回調模式的使用,通過默認實現一個接口創建一個匿名類然后,然后new這個匿名類的實例。
public class AnonymousInnerClass { //訪問規則和局部類一樣 public void test() { //匿名類實現 new Thread(new Runnable() { @Override public void run() { } }).start(); //非匿名類實現 class NoneAnonymousClass implements Runnable{ public void run() { } } NoneAnonymousClass t = new NoneAnonymousClass(); new Thread(t).start(); } }
嵌套類的層次
嵌套類是可以有層次的,也就是說嵌套類里面還是定義類,成為嵌套類中的嵌套類。虛擬機如何保證嵌套類正確的嵌套層層次?
對於merber class,內部嵌套類的可以表示為 A$B 其中A為外部類,B為內部成員類 ,如果B里面又有成員為C的嵌套類,那么C就可以表示為A$B$C,如果A定義了兩個同名member class,那么編譯器就會報錯。如果B里面又包含了為名B的nested class,則編譯器會報錯.
對於local inner Class,局部類可以表示為A$1B的方式,其中A為外部類,B為第一個局部類 如果在不同的方法里面定義了同名的局部類B,編譯器是可以編譯通過的,那么定義的第二個局部類B可以表示為A$2B,如果在同一個方法里面同定義兩個相同的局部類B,那么編譯錯是要報錯的。如果B里面又定義了同名的成員類,則可以表示為A$1B$B。
對於anonymous inner classes,匿名類可以表示為A$1的方式,代表程序里面有一個匿名類。如果有N個,可以表示為A$N的方式(N為自然數).
看看下面的例子
public class NestedClassLevel { class A { // 編譯器會報錯,A里面不能在定義名為A的nested classes // class A{} public void test() { class B { } } } //可以在繼續定義B class B { public void test(){ //可以無限定義匿名類 new Runnable() { public void run() { //可以無限定義匿名類 new Runnable() { public void run() { } }; } }; } } // 只能定義一個B // class B{} public void test() { // 可以定義A class A { public void test() { //可以有同名的局部類B和成員類B class B { public void test() { } } //局部類A里面不能在定義A //class A{} } } //可以有同名的局部類B和成員類B class B { } } }
對於定義在非static上下文里面的nested類層次,比如A$B$1C ,則最內層的嵌套類C有一個指向B實例的引用,B有一個指向A實例的引用,最終最內層的嵌套類可以訪問A中的屬性可以方法,一般把B成為A的直接嵌套類。但是A不可以訪問B或者C中屬性或者方法。
nested interface
由於interface默認是定義為一個 public static的特殊類,所以interface可以直接作為 static member class。可以通過A.B的方式進行訪問。
nested class的應用
在java提供的基本類庫里面,大量使用nested classes。比如我們知道的map類。其中 Map類里面有一個定義了Entry類abstract inner class。所以我們在遍歷map的時候,一般使用
for (Map.Entry entry:map.entrySet()){
}
總結:nested類是java里面比較復雜的一個概念,必須詳細了解jvm中對於嵌套類的實現以及java編譯器對嵌套類的處理才可以深入了解嵌套類細節。