1.內部類的定義
內部類:可以將一個類的定義放在另一個類的定義內部,這就是內部類。
2.創建一個簡單的內部類
具體操作:將類的定義置於外圍類的裏面即可。
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package com; /** * @author [email protected] * TODO 典型的內部類示例 * 典型的內部類中會存在一個方法,該方法返回一個指向內部類的引用, * 普通的內部則不存在這樣的方法 */ public class Pracel1 { class Contents{//內部類1 private int i = 11; public int value(){ return i; } } class Destination{//內部類2 private String label; public Destination(String whereTo){ label = whereTo; } public String readLabel(){ return label; } } //返回內部類1的引用 public Destination to(String to){ return new Destination(to); } //返回內部類2的引用 public Contents contents(){ return new Contents(); } public void ship(String dest){ Contents c = new Contents(); Destination d = new Destination(dest); System.out.println(d.readLabel()); System.out.println(c.value()); } public static void main(String[] args) { Pracel1 p = new Pracel1(); p.ship("wuhu"); } }</span> </span>
3.獲取內部類引用(.new)及獲取外部類引用(.this)
如果要生成外部類對象的引用,則使用外部類名稱.this即可。
如果要生成內部類對象的引用,則使用內部類對象.new即可。在擁有外部類對象之前是不可能創建內部類對象的。因爲內部類對象會默認的連接到創建它的外部類對象上(嵌套類(靜態內部類)除外)。所以,都是先創建外部類對象,然後再創建內部類對象。
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package com; /** * @author [email protected] * TODO 獲取外部類對象的引用、獲取內部類對象的引用 */ public class Outer { void f(){ System.out.println("Outer.f()"); } public class Inner{ //獲取外部類對象的引用 public Outer getOute(){ return Outer.this; } public void i(){ System.out.println("Inner.i()"); } } public static void main(String[] args) { Outer outer = new Outer(); //獲取內部類對象的引用 Outer.Inner inner = outer.new Inner(); inner.i(); } }</span> </span>
4.內部類的特性
在最初,內部類看起來像是一個代碼隱藏機制:將類置於其它類內部。但是,內部類瞭解外部類,並能與之通訊。也就是說,當創建一個內部類對象時,此對象與創造它的外部類之間就會產生一種聯繫,這種聯繫使得內部類能夠訪問外部類的所有成員,而不需要其它任何特殊條件。此外,內部類還擁有其外部類的所有元素的訪問權。爲什麼內部類能夠擁有外部類所有成員的訪問權?(如何才能看到這一幕?)這是怎麼做到的?當某一個外部類對象創建一個內部類對象時,這個內部類對象就會祕密的捕獲一個指向創建它的外部類對象的引用。所以,當訪問外部類對象的成員時,實際上是使用外部類對象的引用來調用該外部類的成員。
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package com; interface Selector{ boolean end(); Object current(); void next(); } /** * @author [email protected] * TODO描述內部類能夠訪問外部類的所有成員的特性 */ public class Sequence { private Object[] items; private int next = 0; public Sequence(){} public Sequence(int size){ items = new Object[size]; } public void add(Object obj){ if(next < items.length) { items[next++] = obj; } } //內部類 private class SequenceSelector implements Selector{ private int i = 0; @Override public boolean end() { // TODO Auto-generated method stub //訪問了外部類的一個私有的成員 return i == items.length; } @Override public Object current() { // TODO Auto-generated method stub return items[i]; } @Override public void next() { // TODO Auto-generated method stub if(i < items.length) { i++; } } } public Selector selector(){ return new SequenceSelector(); } public static void main(String[] args) { Sequence sequence = new Sequence(10); for(int i=0;i<10;i++){ sequence.add(Integer.toString(i)); } //此處向上轉型爲接口 Selector selector = sequence.selector(); while(!selector.end()) { System.out.print(selector.current()+","); selector.next(); } } }</span> </span>
5.內部類與向上轉型
可以向上轉型爲基類或者是接口。當內部類向上轉爲爲基類,尤其是轉型爲接口時,內部類就變得大有用處。因爲此內部類是某一個接口的實現,而此內部類向上轉型後,接口中的實現將完全不可見。所得到的只是指向基類或者接口的引用。示例同上。
6.內部類的繼承
首先,非常明確的一點-內部類也可以繼承。但是內部類的繼承比較麻煩,需要特殊處理。爲什麼內部類的繼承需要特殊處理?因爲內部類的構造器必須連接到指向外部類對象的引用,所以在繼承內部類的時候,必須傳遞外部類對象的引用。
<span style="font-size:12px;">/** * */ package interfaces; class Outer{ class Inner{} } /** * @author [email protected] * TODO 內部類繼承示例 */ public class InheritInner extends Outer.Inner{ //這種傳統的構造方法並不適用 // public InheritInner(){} //必須傳入外部類對象的引用 public InheritInner(Outer outer){ outer.super(); } public static void main(String[] args) { Outer outer = new Outer(); InheritInner ii = new InheritInner(outer); } }</span>
7.內部類可以被覆蓋嗎
如果是直接繼承外部類的話,則內部類並不發生任何事。這兩個內部類是完全獨立的實體,各自在自己的命名空間內。除非是明確的繼承某個內部類,則可以覆蓋其中的方法。
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package inner; class Egg{ protected class Yolk{ public Yolk(){ System.out.println("Egg.Yolk()"); } public void f(){ System.out.println("Egg.Yolk.f()"); } } private Yolk y = new Yolk(); public Egg(){ System.out.println("Egg()"); } public void insertYolk(Yolk y){ this.y = y; } public void g(){ y.f(); } } /** * @author [email protected] * TODO 內部類的覆蓋 */ public class BigEgg extends Egg{ public class Yolk extends Egg.Yolk{ public Yolk(){ System.out.println("BigEgg.Yolk()"); } public void f(){ System.out.println("BigEgg.Yolk.f()"); } } public BigEgg(){ insertYolk(new Yolk()); } public static void main(String[] args) { Egg egg = new BigEgg(); egg.g(); } } //Egg.Yolk() //Egg() //Egg.Yolk() //BigEgg.Yolk() //BigEgg.Yolk.f()</span> </span>
8.內部類標識符
每個類編譯後都會產生一個.class文件,其中包含了如何創建該類型的對象的全部信息(此信息產生一個"meta-class",叫做Class對象)。內部類也是如此,必須產生一個.class文件,以包含它們的Class對象信息。其class文件的命名規則是:
- 外部類名稱+$+內部類的名字。
- 如果內部類是匿名的,則產生一個簡單的數字當作是內部類的名字。
- 如果內部類是嵌套在別的內部類之中,則直接將該內部類的名字加在外部類與$的後面。
示例:
Counter.class(有待理解)
LocalInnerClass$1.class //匿名內部類
LocalInnerClass$1LocalCounter.class
LocalInnerClass.class
9.內部類的分類
內部類的可以大致分爲普通內部類、在方法和作用域內的內部類、匿名內部類、靜態內部類(嵌套類)共5類。
9.1在方法和作用域內的內部類
可以在一個方法裏面或者在任意的作用域內定義內部類。
在方法的作用域內定義的內部類稱爲局部內部類。局部內部類不能有訪問說明符,因爲它不是外部類的一部分,但它能訪問當前代碼塊內的常量以及此外圍類的所有成員。
可以在任意的作用域內嵌入一個內部類。如在方法中的一個if判斷內創建一個內部類。<span style="font-size:12px;">/** * */ package inner; interface Destination{ String readLabel(); } /** * @author [email protected] * TODO 局部內部類(在方法中定義內部類) */ public class Pracel { //方法中定義內部類 public Destination destination(String s){ //內部類 class PDestination implements Destination{ private String label; private PDestination(String whereTo){ label = whereTo; } @Override public String readLabel() { // TODO Auto-generated method stub return label; } } return new PDestination(s); } public static void main(String[] args) { Pracel p = new Pracel(); Destination d = p.destination("wuhu"); } } </span>
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package inner; /** * @author [email protected] * TODO 在方法內部創建內部類 */ public class Pracel2 { private void internalTrack(boolean b){ if(b){ //在方法內部創建內部類 class TrackShip{ private String id; public TrackShip(String id){ this.id = id; } String getShip(){ return id; } } TrackShip ts = new TrackShip("test"); String s = ts.getShip(); } } public void track(){ internalTrack(true); } public static void main(String[] args) { Pracel2 p2 = new Pracel2(); p2.track(); } }</span> </span>
9.2匿名內部類
簡單的說,匿名內部類就是沒有名字的內部類。
<span style="font-size:14px;">/** * */ package inner; /** * @author [email protected] * TODO 匿名內部類 */ public class AnonymousInner { class Contents{ private String contents; public Contents(){ System.out.println("Contents()"); } public Contents(String contents){ this.contents = contents; System.out.println("Contents()"+contents); } public String getContents(){ return contents; } } public Contents contents(final String contents,String contents2){ //匿名內部類 return new Contents(){ //如果在匿名內部類中引用方法中的參數,則參數必須設置爲final類型,以防止在方法內被修改 private String c = contents; };//此分號不是用來標記此內部類結束的。 //實際上,它標記的是表達式的結束, //只不過是這個表達式正好包含的匿名內部類罷了。 } public static void main(String[] args) { AnonymousInner ai = new AnonymousInner(); Contents c = ai.contents("xx","xx2"); } } </span>
9.3 嵌套類(靜態內部類)
static可以聲明屬性和方法,也可以聲明內部類。用static聲明的內部類變成了外部類,但是用static聲明的內部類不能訪問非static的外部類屬性。這種內部類稱爲靜態內部類或者嵌套類。
一般來說,如果不需要內部類對象與其外部類對象之間有聯繫,則可將內部類聲明爲static。通常的內部類對象隱式的保存着創建它的外部類對象的引用,但是靜態內部類則沒有,而且嵌套類中可以包含static方法和域。普通內部類則不能有static方法或域。
<span style="font-size:14px;"><span style="font-size:12px;">package inner; class Outer{ private static String content = "test"; private int i = 10; public static void f(){ System.out.println("Outer.f()"); } //靜態內部類 static class Inner{ public void print(){ //只能訪問外部類的static屬性 System.out.println("content="+content); //只能訪問外部類的static方法 f(); // System.out.println(i); } } } public class StaticInner { public static void main(String[] args) { new Outer.Inner().print(); } }</span> </span>
嵌套類可以作爲接口的一部分(接口中的都是public static的),可以在接口中的嵌套類中使用main方法。可以用來測試接口的實現類(嵌套類進行實現)。
一個嵌套類可以被嵌套多層--其中的內部類可以透明的訪問所有它所嵌入的外圍類的所有成員。<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package inner; /** * @author [email protected] * TODO 接口中的類 */ public interface ClassInInterface { //接口中的抽象方法 public abstract void play(); //嵌套類(默認就是public static) public static class Test implements ClassInInterface{ @Override public void play() { // TODO Auto-generated method stub System.out.println("Impl-play()"); } public static void main(String[] args) { ClassInInterface c = new Test(); c.play(); } } }</span> </span>
<span style="font-size:14px;"><span style="font-size:12px;">/** * */ package inner; /** * @author [email protected] * TODO 多層嵌套類 */ public class Outer { private void outer(){} class A{ private void a(){} private class B{ void b(){ outer(); a(); } } } public static void main(String[] args) { Outer outer = new Outer(); Outer.A a = outer.new A(); Outer.A.B b = a.new B(); } }</span> </span>
10.爲什麼需要內部類?
XXXX