內部類是封裝的一種形式,是定義在類或接口中的類。編程
即定義的內部類做爲外部類的一個普通成員(非static),就像下面這樣:設計模式
public class Outer { class Inner{ private String id = "夏天"; public String getId() { return id; } } public Inner returnInner(){ return new Inner(); } public void show(){ Inner in = new Inner(); System.out.println(in.id); } }
咱們經過以上一個簡單的示例,能夠得出如下幾點:ide
能夠看到,咱們像使用正常類同樣使用內部類,但實際上,內部類有許多奧妙,值得咱們去學習。至於內部類的用處,咱們暫且不談,先學習它的語法也不遲。咱們在另一個類中再試着建立一下這個Inner對象吧:學習
class OuterTest{ public static void main(String[] args) { //!false:Inner in = new Inner(); Outer o = new Outer(); o.show(); Outer.Inner in = o.returnInner(); //!false: can't access --System.out.println(in.id); System.out.println(in.getId()); } }
哦呦,有意思了,咱們在另外一個類OuterTest
中再次測試咱們以前定義的內部類,結果出現了很是明顯的變化,咱們陷入了沉思:測試
Inner in = new Inner();
建立內部類實例。returnInner
方法,來建立一個實例,成功!Outer.Inner
,即外部類名.內部類名
。getId
方法訪問吧,成功!說到這,咱們大概就能猜想到:內部類的存在能夠很好地隱藏一部分具備聯繫代碼,實現了那句話:我想讓你看到的東西你隨便看,不想讓你看的東西你想看,門都沒有。this
其實咱們以前在分析ArrayList
源碼的時候,曾經接觸過內部類。咱們在學習迭代器設計模式的時候,也曾領略過內部類帶了的奧妙之處。下面我經過《Java編程思想》上:經過一個內部類實現迭代器模式的簡單案例作相應的分析與學習:
首先呢,定義一個「選擇器」接口:設計
interface Selector { boolean end();//判斷是否到達終點 void next();//移到下一個元素 Object current();//訪問當前元素 }
而後,定義一個序列類Sequence:指針
public class Sequence { private Object[] items; private int next = 0; //構造器 public Sequence(int size) { items = new Object[size]; } public void add(Object x) { if (next < items.length) { items[next++] = x; } } //該內部類能夠訪問外部類全部成員(包括私有成員) private class SequenceSelector implements Selector { private int i = 0; @Override public boolean end() { return i == items.length; } @Override public void next() { if (i < items.length) { i++; } } @Override public Object current() { return items[i]; } } //向上轉型爲接口,隱藏實現的細節 public Selector selector() { return new SequenceSelector(); } }
SequenceSelector
以private修飾,實現了Selector
接口,提供了方法的具體實現。items
,能夠得出結論:內部類自動擁有對其外部類全部成員的訪問權。當內部類是非static時,當外部類對象建立了一個內部類對象時,內部類對象會產生一個指向外部類的對象的引用,因此非static內部類能夠看到外部類的一切。code
Sequence
的selector
方法返回了一個內部類實例,意思就是用接口類型接收實現類的實例,實現向上轉型,既隱藏了實現細節,又利於擴展。咱們看一下具體的測試方法:
public static void main(String[] args) { Sequence sq = new Sequence(10); for (int i = 0; i < 10; i++) { sq.add(Integer.toString(i)); } //產生咱們設計的選擇器 Selector sl = sq.selector(); while (!sl.end()) { System.out.print(sl.current() + " "); sl.next(); } }
咱們稍微修改一下最初的Outer:
public class Outer { String id = "喬巴"; class Inner{ private String id = "夏天"; public String getId() { return id; } public String getOuterId(){ return Outer.this.id; } public Outer returnOuter(){ return Outer.this; } } public static void main(String[] args) { Outer o = new Outer(); System.out.println(o.new Inner().getId());//夏天 System.out.println(o.new Inner().getOuterId());//喬巴 } }
return Outer.this;
,即外部類名.this
。Outer.this.id
。咱們來測試一波:
public static void main(String[] args) { Outer.Inner oi = new Outer().new Inner(); System.out.println(oi.getId());//夏天 Outer o = oi.returnOuter(); System.out.println(o.id);//喬巴 }
Outer.Inner oi = new Outer().new Inner();
,即在外部類對象後面用.new 內部類構造器
。咱們對內部類指向外部類對象的引用進行更加深刻的理解與體會,咱們會發現,上面的代碼在編譯以後,會產生兩個字節碼文件:
Outer$Inner.class
和Outer.class
。咱們對Outer$Inner.class
進行反編譯:
確實,內部類在建立的過程當中,依靠外部類對象,並且會產生一個指向外部類對象的引用。
即在方法做用域內建立一個完整的類。
public class Outer { public TestOuter test(final String s){ class Inner implements TestOuter{ @Override public void testM() { //!false: s+="g"; System.out.println(s); } } return new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.test("天喬巴夏").testM();//天喬巴夏 } } interface TestOuter{ void testM(); }
須要注意兩點:
JDK1.8以後能夠不用final顯式修飾傳入參數和局部變量,但其自己仍是至關於final修飾的,不可改變。咱們去掉final,進行反編譯:
能夠將內部類定義在任意的做用域內:
public class Outer { public void test(final String s,final int value){ final int a = value; if(value>2){ class Inner{ public void testM() { //!false: s+="g"; //!false: a+=1; System.out.println(s+", "+a); } } Inner in = new Inner(); in.testM(); } //!false:Inner i = new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.test("天喬巴夏",3); } }
一樣須要注意的是:
即用static
修飾的成員內部類,歸屬於類,即它不存在指向外部類的引用。
public class Outer { static int a = 5; int b = 6; static class Inner{ static int value; public void show(){ //!false System.out.println(b); System.out.println(a); } } } class OuterTest { public static void main(String[] args) { Outer.Inner oi = new Outer.Inner(); oi.show(); } }
須要注意的是:
new Outer.Inner()
建立內部類對象。static final
修飾)。這個類型的內部類,看着名字就怪怪的,咱們先看看一段違反咱們認知的代碼:
public class Outer { public InterfaceInner inner(){ //建立一個實現InterfaceInner接口的是實現類對象 return new InterfaceInner() { @Override public void show() { System.out.println("Outer.show"); } }; } public static void main(String[] args) { Outer o = new Outer(); o.inner().show(); } } interface InterfaceInner{ void show(); }
真的很是奇怪,乍一看,InterfaceInner
是個接口,而Outer類的inner方法怎麼出現了new InterfaceInner()
的字眼呢?接口不是不能建立實例對象的麼?
確實,這就是匿名內部類的一個使用,其實inner方法返回的是實現了接口方法的實現類對象,咱們能夠看到分號結尾,表明一個完整的表達式,只不過表達式包含着接口實現,有點長罷了。因此上面匿名內部類的語法其實就是下面這種形式的簡化形式:
public class Outer { class Inner implements InterfaceInner{ @Override public void show(){ System.out.println("Outer.show"); } } public InterfaceInner inner(){ return new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.inner().show(); } } interface InterfaceInner{ void show(); }
不只僅是接口,普通的類也能夠被看成「接口」來使用:
public class Outer { public OuterTest outerTest(int value) { //參數傳給匿名類的基類構造器 return new OuterTest(value) { @Override public int getValue() { return super.getValue() * 10; } }; } public static void main(String[] args) { Outer o = new Outer(); System.out.println(o.outerTest(10).getValue());//100 } } class OuterTest { public int value; OuterTest(int value) { this.value = value; } public int getValue() { return value; } }
須要注意的是:
內部類能夠被繼承,可是和咱們普通的類繼承有些出處。具體來看一下:
public class Outer { class Inner{ private int value = 100; Inner(){ } Inner(int value){ this.value = value; } public void f(){ System.out.println("Inner.f "+value); } } } class TestOuter extends Outer.Inner{ TestOuter(Outer o){ o.super(); } TestOuter(Outer o,int value){ o.super(value); } public static void main(String[] args) { Outer o = new Outer(); TestOuter tt = new TestOuter(o); TestOuter t = new TestOuter(o,10); tt.f(); t.f(); } }
咱們能夠發現的是:
class A extends Outer.Inner{}
。o.super();
,即都須要傳入外部類對象做爲參數。能夠看到的一點就是,內部類內部的實現細節能夠被很好地進行封裝。並且Java中存在接口的多實現,雖然必定程度上彌補了Java「不支持多繼承」的特色,但內部類的存在使其更加優秀,能夠看看下面這個例子:
//假設A、B是兩個接口 class First implements A{ B makeB(){ return new B() { }; } }
這是一個經過匿名內部類實現接口功能的簡單的例子。對於接口而言,咱們徹底能夠經過下面這樣進行,由於Java中一個類能夠實現多個接口:
class First implements A,B{ }
可是除了接口以外,像普通的類,像抽象類,均可以定義獨立的內部類去單獨繼承並實現,使用內部類使「多重繼承」更加完善。
因爲後面的許多內容尚未涉及到,學習到,因此總結的比較淺顯,並無作特別深刻,特別真實的場景模擬,以後有時間會再作系統性的總結。若是有敘述錯誤的地方,還望評論區批評指針,共同進步。
參考:
《Java 編程思想》
https://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class?r=SearchResults