Java Inner Class 内部类
內(nèi)部類(lèi) ?Inner Class
一個(gè)內(nèi)部類(lèi)可以定義在另一個(gè)類(lèi)里,可以定義在函數(shù)里,甚至可以作為一個(gè)表達(dá)式的一部分。
Java中的內(nèi)部類(lèi)共分為四種:
靜態(tài)內(nèi)部類(lèi)static inner class (also called nested class)
成員內(nèi)部類(lèi)member inner class
局部?jī)?nèi)部類(lèi)local inner class
匿名內(nèi)部類(lèi)anonymous inner class
?
1?成員內(nèi)部類(lèi) ?member inner class
?
1.1 形式
成員內(nèi)部類(lèi)也是定義在另一個(gè)類(lèi)中,但是定義時(shí)不用static修飾。形如:
1 class Outer { 2 class Inner{ 3 4 } 5 }編譯之后會(huì)產(chǎn)生如下2個(gè)class文件:
?
---->這可以將相關(guān)的類(lèi)組織在一起,從而降低了命名空間的混亂。
?
成員內(nèi)部類(lèi)的修飾符:
對(duì)于普通的類(lèi),可用的修飾符有final、abstract、strictfp、public和默認(rèn)的包訪問(wèn)。
但是成員內(nèi)部類(lèi)更像一個(gè)成員變量和方法。
可用的修飾符有:final、abstract、public、private、protected、strictfp和static。
一旦用static修飾內(nèi)部類(lèi),它就變成靜態(tài)內(nèi)部類(lèi)了。
?
1.2 創(chuàng)建內(nèi)部類(lèi)實(shí)例
成員內(nèi)部類(lèi)就像一個(gè)實(shí)例變量,他依賴(lài)于外部類(lèi)的實(shí)例存在 --> 必須先有外部類(lèi)的實(shí)例 ?才能創(chuàng)建成員內(nèi)部類(lèi)對(duì)象
在外部類(lèi)里面創(chuàng)建成員內(nèi)部類(lèi)的實(shí)例:this.new Innerclass(); 或可以用 Inner inner = new Inner(); 方法直接創(chuàng)建
在外部類(lèi)之外創(chuàng)建內(nèi)部類(lèi)的實(shí)例:(new Outerclass()).new Innerclass(); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 或 ??Inner inner = new Outer().new Inner()
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 或 ??Outer outer = new Outer(); Inner inner = outer.new Inner();
案例:從外部類(lèi)的非靜態(tài)方法中實(shí)例化內(nèi)部類(lèi)對(duì)象。
1 class Outer { 2 private int i = 10; 3 public void makeInner(){ 4 Inner in = new Inner(); 5 in.seeOuter(); 6 } 7 class Inner{ 8 public void seeOuter(){ 9 System.out.print(i); 10 } 11 } 12 }表面上,我們并沒(méi)有創(chuàng)建外部類(lèi)的對(duì)象就實(shí)例化了內(nèi)部類(lèi)對(duì)象,和上面的話矛盾。
事實(shí)上,如果不創(chuàng)建外部類(lèi)對(duì)象也就不可能調(diào)用makeInner()方法,所以到頭來(lái)還是要?jiǎng)?chuàng)建外部類(lèi)對(duì)象的。
你可能試圖把makeInner()方法修飾為靜態(tài)方法,即static public void makeInner()。
這樣不創(chuàng)建外部類(lèi)就可以實(shí)例化外部類(lèi)了!但是在一個(gè)靜態(tài)方法里能訪問(wèn)非靜態(tài)成員和方法嗎?顯然不能。
-->?必須先有外部類(lèi)的實(shí)例 ?才能創(chuàng)建成員內(nèi)部類(lèi)對(duì)象
案例:從外部類(lèi)的靜態(tài)方法中實(shí)例化內(nèi)部類(lèi)對(duì)象
1 class Outer { 2 private int i = 10; 3 class Inner{ 4 public void seeOuter(){ 5 System.out.print(i); 6 } 7 } 8 public static void main(String[] args) { 9 Outer out = new Outer(); 10 Outer.Inner in = out.new Inner(); 11 //Outer.Inner in = new Outer().new Inner(); 12 in.seeOuter(); 13 } 14 }被注釋掉的那行是它上面兩行的合并形式,一條簡(jiǎn)潔的語(yǔ)句。
對(duì)比一下:在外部類(lèi)的非靜態(tài)方法中實(shí)例化內(nèi)部類(lèi)對(duì)象是普通的new方式:Inner in = new Inner();
在外部類(lèi)的靜態(tài)方法中實(shí)例化內(nèi)部類(lèi)對(duì)象,必須先創(chuàng)建外部類(lèi)對(duì)象:Outer.Inner in = new Outer().new Inner();
?
1.3 成員內(nèi)部類(lèi)操作外部類(lèi)
?
成員內(nèi)部類(lèi)可以訪問(wèn)它的外部類(lèi)的所有成員變量和方法,不管是靜態(tài)的還是非靜態(tài)的都可以。
內(nèi)部類(lèi)就像一個(gè)實(shí)例成員一樣存在于外部類(lèi),所以?xún)?nèi)部類(lèi)可以訪問(wèn)外部類(lèi)的所有成員就想訪問(wèn)自己的成員一樣沒(méi)有限制。
?
內(nèi)部類(lèi)中的this指的是內(nèi)部類(lèi)的實(shí)例對(duì)象本身,如果要用外部類(lèi)的實(shí)例對(duì)象就可以用類(lèi)名.this的方式獲得。
普通的類(lèi)可以用this引用當(dāng)前的對(duì)象,內(nèi)部類(lèi)也是如此。
但是假若內(nèi)部類(lèi)想引用外部類(lèi)當(dāng)前的對(duì)象呢?用“外部類(lèi)名”.this;的形式,如下例的Outer.this。
1.4?內(nèi)部類(lèi)對(duì)象中不能有靜態(tài)成員
原因很簡(jiǎn)單,內(nèi)部類(lèi)的實(shí)例對(duì)象是外部類(lèi)實(shí)例對(duì)象的一個(gè)成員,若沒(méi)有外部類(lèi)對(duì)象,內(nèi)部類(lèi)就不會(huì)存在,何談靜態(tài)成員呢。
1 class Outer { 2 class Inner{ 3 static int i = 0; 4 public void seeOuter(){ 5 System.out.println(this); 6 System.out.println(Outer.this); 7 } 8 } 9 10 public static void main(String[] strs){ 11 new Outer().new Inner().seeOuter(); 12 } 13 }我們編譯這個(gè)類(lèi):
1 E:\>javac Outer.java 2 Outer.java:3: 內(nèi)部類(lèi)不能有靜態(tài)聲明 3 static int i = 0; 4 ^ 5 1 錯(cuò)誤?
2?局部?jī)?nèi)部類(lèi)local inner class
?
局部?jī)?nèi)部類(lèi)local inner class 也可以成為方法內(nèi)部類(lèi)
顧名思義,就是把類(lèi)放在方法內(nèi)。局部?jī)?nèi)部類(lèi)定義在方法中,比方法的范圍還小。是內(nèi)部類(lèi)中最少用到的一種類(lèi)型。
像局部變量一樣,不能被public, protected, private和static修飾。
局部?jī)?nèi)部類(lèi)在方法中定義,所以只能在方法中使用,即只能在方法當(dāng)中生成局部?jī)?nèi)部類(lèi)的實(shí)例并且調(diào)用其方法。
1 class Outer { 2 public void doSomething(){ 3 class Inner{ 4 public void seeOuter(){ 5 System.out.println("inner class"); 6 } 7 } 8 9 Inner inner = new Inner(); 10 inner.seeOuter(); 11 } 12 13 public static void main(String ... args){ 14 new Outer().doSomething(); 15 } 16 }輸出:
inner class局部?jī)?nèi)部類(lèi)只能在聲明的方法內(nèi)是可見(jiàn)的,因此定義局部?jī)?nèi)部類(lèi)之后,想用的話就要在方法內(nèi)直接實(shí)例化,
記住這里順序不能反了,一定是要先聲明后使用,否則編譯器會(huì)說(shuō)找不到。
方法內(nèi)部類(lèi)的修飾符:
? 與成員內(nèi)部類(lèi)不同,方法內(nèi)部類(lèi)更像一個(gè)局部變量。
? 可以用于修飾方法內(nèi)部類(lèi)的只有final和abstract。
注意事項(xiàng):
A: 方法內(nèi)部類(lèi)只能在定義該內(nèi)部類(lèi)的方法內(nèi)實(shí)例化,不可以在此方法外對(duì)其實(shí)例化
B: 方法內(nèi)部類(lèi)對(duì)象不能使用該內(nèi)部類(lèi)所在方法的非final局部變量。
原因:
? ? ?因?yàn)榉椒ǖ木植孔兞课挥跅I?#xff0c;只存在于該方法的生命期內(nèi)。當(dāng)一個(gè)方法結(jié)束,其棧結(jié)構(gòu)被刪除,局部變量成為歷史。
? ? 但是該方法結(jié)束之后,在方法內(nèi)創(chuàng)建的內(nèi)部類(lèi)對(duì)象可能仍然存在于堆中!例如,如果對(duì)它的引用被傳遞到其他某些代碼,并存儲(chǔ)在一個(gè)成員變量?jī)?nèi)。
? ? 正因?yàn)椴荒鼙WC局部變量的存活期和方法內(nèi)部類(lèi)對(duì)象的一樣長(zhǎng),所以?xún)?nèi)部類(lèi)對(duì)象不能使用它們。下面是完整的例子:
? C:靜態(tài)方法內(nèi)的方法內(nèi)部類(lèi)。
? ? ?靜態(tài)方法是沒(méi)有this引用的,因此在靜態(tài)方法內(nèi)的內(nèi)部類(lèi)遭受同樣的待遇,即:只能訪問(wèn)外部類(lèi)的靜態(tài)成員。
?
3?匿名內(nèi)部類(lèi)Anonymous Inner Class
顧名思義,沒(méi)有名字的內(nèi)部類(lèi)。
匿名內(nèi)部類(lèi)就是沒(méi)有名字的局部?jī)?nèi)部類(lèi),不使用關(guān)鍵字class, extends, implements, 沒(méi)有構(gòu)造方法。
匿名內(nèi)部類(lèi)隱式地繼承了一個(gè)父類(lèi)或者實(shí)現(xiàn)了一個(gè)接口。
匿名內(nèi)部類(lèi)使用得比較多,通常是作為一個(gè)方法參數(shù)。
?
A、繼承式的匿名內(nèi)部類(lèi)。
1 class Car { 2 public void drive(){ 3 System.out.println("Driving a car!"); 4 } 5 } 6 7 8 class Test{ 9 public static void main(String[] args) { 10 Car car = new Car(){ 11 public void drive(){ 12 System.out.println("Driving another car!"); 13 } 14 }; 15 car.drive(); 16 } 17 }?
結(jié)果輸出了:Driving another car!
建立匿名內(nèi)部類(lèi)的關(guān)鍵點(diǎn)是重寫(xiě)父類(lèi)的一個(gè)或多個(gè)方法。再?gòu)?qiáng)調(diào)一下,是重寫(xiě)父類(lèi)的方法,而不是創(chuàng)建新的方法。
因?yàn)橛酶割?lèi)的引用不可能調(diào)用父類(lèi)本身沒(méi)有的方法!創(chuàng)建新的方法是多余的。簡(jiǎn)言之,參考多態(tài)。
?
B、接口式的匿名內(nèi)部類(lèi)。
1 interface Vehicle { 2 public void drive(); 3 } 4 5 6 class Test{ 7 public static void main(String[] args) { 8 Vehicle v = new Vehicle(){ 9 public void drive(){ 10 System.out.println("Driving a car!"); 11 } 12 }; 13 v.drive(); 14 }這種形式的代碼我們一定寫(xiě)過(guò),這也是內(nèi)部類(lèi)的存在的一個(gè)重要作用:便于編寫(xiě) 線程和事件驅(qū)動(dòng)的代碼
1 public class ThreadDemo { 2 public static void main(String[] args) { 3 4 new Thread(new Runnable(){ 5 public void run(){ 6 System.out.println("Hello World!"); 7 } 8 }).start(); 9 } 10 } 11 12 13 E:\>javac ThreadDemo.java 14 E:\>java ThreadDemo 15 Hello World! 1 public class SwingTest 2 { 3 public static void main(String[] args) 4 { 5 JFrame frame = new JFrame("JFrame"); 6 JButton button = new JButton("JButton"); 7 8 button.addActionListener(new ActionListener(){ 9 10 @Override 11 public void actionPerformed(ActionEvent arg0){ 12 System.out.println("Hello World"); 13 14 } 15 }); 16 17 frame.getContentPane().add(button); 18 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 19 frame.setSize(200, 200); 20 21 frame.addWindowListener(new WindowAdapter() { 22 23 @Override 24 public void windowClosing(WindowEvent e){ 25 System.out.println("Closing"); 26 System.exit(0); 27 } 28 }); 29 30 frame.setVisible(true); 31 } 32 }若你了解安卓的話 ?會(huì)發(fā)現(xiàn)這樣的代碼編寫(xiě)方式有著很多的應(yīng)用
好處就是簡(jiǎn)化我們的代碼。
?
C、參數(shù)式的匿名內(nèi)部類(lèi)。
1 class Bar{ 2 void doStuff(Foo f){} 3 } 4 5 inteface Foo{ 6 void foo(); 7 } 8 9 class Test{ 10 static void go(){ 11 Bar b = new Bar(); 12 b.doStuff(new Foo(){ 13 public void foo(){ 14 System.out.println("foofy"); 15 } 16 }); 17 } 18 } 19?
4?靜態(tài)內(nèi)部類(lèi)static inner class?
?
在定義成員內(nèi)部類(lèi)的時(shí)候,可以在其前面加上一個(gè)權(quán)限修飾符static。此時(shí)這個(gè)內(nèi)部類(lèi)就變?yōu)榱遂o態(tài)內(nèi)部類(lèi)。
同樣會(huì)被編譯成一個(gè)完全獨(dú)立的.class文件,名稱(chēng)為OuterClass$InnerClass.class的形式。
只可以訪問(wèn)外部類(lèi)的靜態(tài)成員和靜態(tài)方法,包括了私有的靜態(tài)成員和方法。
生成靜態(tài)內(nèi)部類(lèi)對(duì)象的方式為:OuterClass.InnerClass inner = new OuterClass.InnerClass();
?
靜態(tài)內(nèi)部類(lèi)使用代碼:
1 package com.learnjava.innerclass; 2 3 class StaticInner 4 { 5 private static int a = 4; 6 7 // 靜態(tài)內(nèi)部類(lèi) 8 public static class Inner 9 { 10 public void test() 11 { 12 // 靜態(tài)內(nèi)部類(lèi)可以訪問(wèn)外部類(lèi)的靜態(tài)成員 13 // 并且它只能訪問(wèn)靜態(tài)的 14 System.out.println(a); 15 } 16 17 } 18 } 19 20 public class StaticInnerClassTest 21 { 22 23 public static void main(String[] args) 24 { 25 StaticInner.Inner inner = new StaticInner.Inner(); 26 inner.test(); 27 } 28 }?
與一般內(nèi)部類(lèi)不同,在靜態(tài)代碼中不能夠使用this操作,所以在靜態(tài)內(nèi)部類(lèi)中只可以訪問(wèn)外部類(lèi)的靜態(tài)變量和靜態(tài)方法。
使用靜態(tài)內(nèi)部類(lèi)的目的和使用內(nèi)部類(lèi)相同。如果一個(gè)內(nèi)部類(lèi)不依賴(lài)于其外部類(lèi)的實(shí)例變量,或與實(shí)例變量無(wú)關(guān),則選擇應(yīng)用靜態(tài)內(nèi)部類(lèi)。
可以在靜態(tài)內(nèi)部類(lèi)的方法中,直接訪問(wèn)外部類(lèi)的靜態(tài)變量和調(diào)用靜態(tài)方法。但不允許訪問(wèn)外部類(lèi)的實(shí)例變量以及實(shí)例方法。
靜態(tài)內(nèi)部類(lèi)的實(shí)例方法中亦只允許訪問(wèn)外部類(lèi)的靜態(tài)成員。
?
靜態(tài)內(nèi)部類(lèi)不同于其他3種內(nèi)部類(lèi),他有著自己特殊的特性,參看:解析靜態(tài)內(nèi)部類(lèi)的使用目的與限制
?
5 小結(jié)
?
5.1?幾種內(nèi)部類(lèi)的共性:
A、內(nèi)部類(lèi)仍然是一個(gè)獨(dú)立的類(lèi),在編譯之后會(huì)內(nèi)部類(lèi)會(huì)被編譯成獨(dú)立的.class文件,但是前面冠以外部類(lèi)的類(lèi)命和$符號(hào)。
B、內(nèi)部類(lèi)不能用普通的方式訪問(wèn)。內(nèi)部類(lèi)是外部類(lèi)的一個(gè)成員,因此內(nèi)部類(lèi)可以自由地訪問(wèn)外部類(lèi)的成員變量,無(wú)論是否是private的。
?
5.2?java中為什么要引入內(nèi)部類(lèi)?還有匿名內(nèi)部類(lèi)?
?
1)可以是單繼承的一種補(bǔ)充解決方案 inner classes能有效實(shí)際地允許“多重實(shí)現(xiàn)繼承(multiple implementation)”
? ?Java中一個(gè)類(lèi)只能繼承一個(gè)類(lèi) 可以通過(guò)內(nèi)部類(lèi)達(dá)到繼承多個(gè)類(lèi)的效果
? ?:每個(gè)inner class都能夠各自繼承某一實(shí)現(xiàn)類(lèi)(implementation),因此,inner class不受限于outer class是否已繼承自某一實(shí)現(xiàn)類(lèi)。?
2)針對(duì)具體的問(wèn)題提供具體的解決方案,同時(shí)又能對(duì)外隱藏實(shí)現(xiàn)細(xì)節(jié) ? ?看具體的案例
?
案例1
在集合中可以使用Iterator遍歷 但每一種集合的數(shù)據(jù)結(jié)構(gòu)不同 ?導(dǎo)致遍歷的方法必然也不同
所以Java在每個(gè)具體的集合里定義了一個(gè)內(nèi)部類(lèi)Itr ?他實(shí)現(xiàn)了Iterator接口 ? 從而根據(jù)所在類(lèi)的具體情況進(jìn)行遍歷
Itr是private的類(lèi) 對(duì)外并不可見(jiàn) 因?yàn)槲业谋闅v方法我自己知道就可以了 別人并不需要了解
集合類(lèi)有實(shí)現(xiàn)了Tterable接口 通過(guò)里面的iterator方法獲取該內(nèi)部類(lèi)實(shí)例 外部不能直接創(chuàng)建該內(nèi)部類(lèi)的實(shí)例
?
案例2
為某個(gè)類(lèi)提供特定的數(shù)據(jù)結(jié)構(gòu) :? 為了共同解決一個(gè)具體的問(wèn)題 但又可以對(duì)外部保持透明
如HashMap中有Entry ? ConcurrentHashMap有HashEntry 和Segment ?
看HashMap中的內(nèi)部類(lèi):
1 public class HashMap<K,V> 2 extends AbstractMap<K,V> 3 implements Map<K,V>, Cloneable, Serializable 4 { 5 6 static final Entry<?,?>[] EMPTY_TABLE = {}; 7 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; 8 9 static class Entry<K,V> implements Map.Entry<K,V> { 10 final K key; 11 V value; 12 Entry<K,V> next; 13 int hash; 14 // ... ... 15 } 16 }關(guān)于HashMap 可以參考:HashMap源碼解析
關(guān)于ConcurrentHashMap:ConcurrentHashMap??ConcurrentHashMap原理分析
轉(zhuǎn)載于:https://www.cnblogs.com/wihainan/p/4773090.html
總結(jié)
以上是生活随笔為你收集整理的Java Inner Class 内部类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 招商银行优酷联名信用卡怎么申请?申卡渠道
- 下一篇: Java:switch语句例子