1.內部類訪問外圍類的成員方法
- 內部類可以訪問其外圍類的方法和字段,就像他們自己的一樣。
- 當某個外圍類的對象創建一個內部類對象時,此內部類對象必定會祕密地捕獲一個指向那個外圍類對象的引用。
在訪問此外圍類成員的時候,就用那個引用去訪問外圍類的成員,內部類對象只能在與其外圍類的對象相關聯
的時候才能被創建。編譯器會處理這些細節。
2.使用.this和.new
- .this生成一個外部類對象的引用。
public class DotThis { void f(){System.out.println("DotThis.f()");} public class Inner{ public DotThis outer(){ //.this 返回外部類對象 return DotThis.this; } } public Inner inner(){return new Inner();} public static void main(String[] args) { DotThis dt = new DotThis(); Inner dti = dt.inner(); dti.outer().f(); } }
- .new 生成內部類的對象。
可以看到上面的代碼必須生成一個外部類對象,然後才能生成內部類對象。public class DotNew { public class Inner{} public static void main(String[] args) { DotNew dn = new DotNew(); //注意這裏 你不能new DotNew.Inner(); Inner dni = dn.new Inner(); } }
因爲在擁有外部類對象之前是不可能創建內部類對象的。這是因爲內部類對象會隱式的連接到創建它的外部類對象上。
3.內部類和向上轉型
- 當內部類向上轉型爲其父類的時候,得到的只是指向父類或接口的引用,所以只能調用父類或接口中所聲明的方法(子類會重寫這些方法),所以就隱藏了實現細節
而聲明private或protected(除非它的子類或同一包下的或外部類能訪問)的內部類可以實現外部類外的類不能聲明這個內部類的具體引用,所以內部類就實現了細節的隱藏。interface Destination{ String readLabel(); } interface Contents{ int value(); } class Parcel{ //內部類 private class PContents implements Contents{ private int i = 11; public int value() {return i;} } //內部類 protected class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return null;} } public Destination destination(String s){return new PDestination(s);} public Contents contents(){return new PContents();} } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); //通過p的方法獲得,進行了向上轉型 Contents c = p.contents(); // c = p.new PContents();這種是錯誤的行爲,因爲PContents是Parcel的私有內部類 Destination d = p.destination("哈哈"); } }
4.內部類的複雜使用
- 內部類語法 覆蓋了大量其他的更加難以理解的技術。
- 創建一個類,但又不希望這個類是公共可用的。像3一樣,private的內部類。
- 一個定義在方法中的類。
- 一個定義在作用域的類,此作用域在方法的內部。
- 一個實現了接口的匿名類。
- 一個匿名類,它擴展了有非默認構造器的類。
- 一個匿名類,他執行字段初始化。
- 一個匿名類,他通過實例化實現構造器(匿名類不可能有構造器)。
4.1.一個定義在方法(域)中的內部類
- 定義在方法中的內部類在方法外是不可以訪問這個內部類的。
interface Destination{ String readLabel(); } class Parcel{ //方法 public Destination destination(String s){ //內部類 class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return label;} } //返回這個對象,可以寫一個引用獲得這個對象 return new PDestination(s); } } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); Destination d = p.destination("呵呵"); } }
4.2.定義在作用域內的類
- 定義在作用域內的類,在作用域外是不能夠訪問這個類的。
public class Parcel6 { private void internalTracking(boolean b){ if (b) { //定義在作用域內的類,作用域外不可訪問 class TrackingSlip{ private String id; TrackingSlip(String s){ id = s; } String getSlip(){return id;} } TrackingSlip ts = new TrackingSlip("x"); String s = ts.getSlip(); } } public void track(){internalTracking(true);} public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } }
4.3實現了接口匿名內部類
- 匿名內部類一定伴隨着抽象類或接口的繼承。
上面是下面簡化形式:??有問題interface Contents{ int value(); } public class TestParcel { public Contents contents(){ //可以看到下面這個實現了Contents接口的類並沒有名字,它看起來像是創建了一個Contents對象。 return new Contents() { private int i = 11; public int value() {return i;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
interface Contents{ int value(); } public class TestParcel { class MyContents implements Contents{ private int i = 11; public int value() {return i;} } public Contents contents(){return new MyContents();} public static void main(String[] args) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
4.4擴展了非默認構造器的匿名內部類
class Wrapping{//這是一個普通的類 private int i; public Wrapping(int x){i = x;} public int value(){return i;} } public class Parcel7 { public Wrapping wrapping(int x){ //也可以這樣 return new Wrapping(x){ public int value() { return super.value()*47; } };//此分號和其他return表達式的分號一樣,代表表達式結束。 } }
4.5執行字段初始化的內部類
- 如果一個匿名內部類希望使用一個在其外定義的對象,那麼編譯器會要求其參數引用是final的。
interface Destination{ String readLabel(); } public class TestParcel { //final的參數 public Destination destination(final String dest){ return new Destination() { //初始化 private String label = dest; public String readLabel() {return label;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Destination d = p.destination("哈哈"); } }
4.6實例化實現構造的匿名內部類
- 匿名內部類中不可能有命名構造器(因爲它們根本就沒有名字),但可以通過實力初始化,就能夠達到爲匿名內部類創建一個構造器的效果,但並不是一個構造器,只是達到效果。
abstract class Base{ public Base(int i){ System.out.println("Base constructor, i = " + i); } public abstract void f(); } public class AnonymousConstructor { //參數i不必爲final ,因爲沒有在匿名內部類中使用 public static Base getBase(int i){ return new Base(i) { //使用這種方式實現實例化構造, 實例初始化 {System.out.println("Inside instance initializer");} public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } }
- 實例初始化的實際效果就是構造器。但它受到了限——你不能重載實例初始化方法,所以你僅有一個這樣的構造器。
4.7匿名內部類的限制
- 匿名內部類既可以擴展類,也可以實現接口,但不能兩者兼備。而且如果是實現接口,也只能實現一個。
- 匿名內部類可以實現工廠模式。
5.嵌套類
- 如果不需要內部類對象與外部類對象之間有聯繫,就將內部類聲明爲static。通常被稱爲嵌套類。
- 普通的內部類對象是隱式的保存了一個指向外圍類對象的引用。當內部類是static意味着
要創建嵌套類的對象,並不需要依賴其外圍類的對象
不能從嵌套類的對象中訪問非靜態的外圍類對象。 - 普通內部類的字段和方法,只能放在類的外部層次上,所以普通的內部類不能有static數據和static字段,也不能包含嵌套類。但嵌套類可以。
interface Destination{ String readLabel(); } interface Contents{ int value(); } public class TestParcel { //嵌套類 private static class ParcelContents implements Contents{ private int i = 11; public int value() {return i;} } //嵌套類 protected static class ParcelDestination implements Destination{ private String label; //private的構造方法 這表示外面是無法初始化的 private ParcelDestination(String whereTo){ label = whereTo; } public String readLabel() {return label;} //定義一些靜態方法 public static void f(){} static int x = 10; //嵌套類在內部再次嵌套一個類 static class AnotherLevel{ public static void f(){} static int x = 10; } } public static Destination destination(String s){ return new ParcelDestination(s); } public static Contents contents(){ return new ParcelContents(); } public static void main(String[] args) { //可以看到,這裏獲取嵌套的內部類並不需要外部類的支持了,內部類不需要與外部類關聯才能創建了。 Contents c = contents(); Destination d = destination("呵呵"); } }
6.接口內部的類
- 放到接口中的任何類都是public和static的。因爲是static的,只是將嵌套類置於接口的命名空間內。
public interface ClassInInterface { void howdy(); //內部類中實現外圍接口。 class Test implements ClassInInterface{ public void howdy() { System.out.println("Howdy!"); } public static void main(String[] args) { new Test().howdy(); } } }
- 如果你想要創建某些公共方法,使得他們能夠被某個接口的所有不同實現公用,那麼使用接口嵌套類會更方便。
7.從多層嵌套類中訪問外部類的成員
- 一個內部類被嵌套多少層並不重要——它能夠透明的訪問所有它嵌入的外圍類的所有成員。
class MNA{ private void f(){} //一層嵌套 class A{ private void g(){} //二層嵌套 public class B{ void h(){ //調用外部類的方法,不需要任何條件 g(); f(); } } } } public class MultiNestingAccess { public static void main(String[] args) { MNA mna = new MNA(); //通過外部類創建內部類 MNA.A mnaa = mna.new A(); //二層嵌套 MNA.A.B mnaab = mnaa.new B(); mnaab.h(); } }