內部類(inner class)是定義在另外一個類中的類。html
- 內部類方法能夠訪問該類定義所在的做用域中的數據,包括私有的數據。
- 內部類能夠對同一個包中的其餘類隱藏起來
- 當想定義一個回調函數且不想編寫大量代碼時,使用匿名(anonymous)內部類比較便捷。
如下簡單舉例:java
一個時鐘須要兩個參數:發佈通告的時間間隔以及開關鈴聲的標誌。ide
public class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval, boolean beep){ this.interval = interval; this.beep = beep; } public void start(){ ActionListener listener = new TimePrinter(); Timer timer = new Timer(interval, listener); timer.start(); } public class TimePrinter implements ActionListener{ @Override public void actionPerformed(ActionEvent e) { System.out.println("At the tone, the time is " + new Date()); if (beep) { Toolkit.getDefaultToolkit().beep(); } } } public static void main(String[] args) { TalkingClock clock = new TalkingClock(1000, true); clock.start(); //keep program running until user selects "OK" JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } }
本代碼主要想說明的是做爲內部類的TimePrinter能夠直接訪問外部類TalkingClock的私有成員變量beep。函數
語法和用法你們很容易就學會了,那麼內部類是如何實現直接訪問其餘類的私有成員變量呢?咱們知道Java的私有成員變量只有類的實例自己能訪問。學習
在最初學習Java語法的時候,是把內部類當作該類的一個屬性去記憶和理解的,所以,天然能夠訪問私有變量。這個理解,如今看來是錯誤的。ui
內部類是一種編譯器現象,與虛擬機無關。編譯器將會把內部類翻譯成用$分隔外部類名與內部類名的常規類文件,而虛擬機對此一無所知。this
上述內部類被編譯成TalkingClock$TimePrinter :spa
javap -private TalkingClock\$TimePrinter.class Compiled from "TalkingClock.java" public class com.test.java.clazz.innerclass.TalkingClock$TimePrinter implements java.awt.event.ActionListener { final com.test.java.clazz.innerclass.TalkingClock this$0; public com.test.java.clazz.innerclass.TalkingClock$TimePrinter(com.test.java.clazz.innerclass.TalkingClock); public void actionPerformed(java.awt.event.ActionEvent); }
能夠看到,多了一個成員變量 final com.test.java.clazz.innerclass.TalkingClock this$0;
,以及構造函數。內部類將經過這種方式持有外部類。翻譯
內部類的一個特性是能夠訪問外部的私有成員變量,這又是如何作到的呢。code
查看外部類:
javap -private TalkingClock.class Compiled from "TalkingClock.java" public class com.test.java.clazz.innerclass.TalkingClock { private int interval; private boolean beep; public com.test.java.clazz.innerclass.TalkingClock(int, boolean); public void start(); public static void main(java.lang.String[]); static boolean access$000(com.test.java.clazz.innerclass.TalkingClock); }
能夠發現,編譯器給添加了新的靜態方法static boolean access$000(com.test.java.clazz.innerclass.TalkingClock);
,顯然,咱們能夠經過類名調用這個方法來獲取私有變量,至關於給添加了getter方法暴露私有變量。
查看變異後的內部類的actionPerformed方法:
public void actionPerformed(ActionEvent e) { System.out.println("At the tone, the time is " + new Date()); if (TalkingClock.access$000(this.this$0)) { Toolkit.getDefaultToolkit().beep(); } }
綜上,內部類經過編譯器修改代碼,實現了對類的訪問。若是黑客在同包名下調用外部類的access$000,則是能夠訪問到私有變量的,這算是一個危險的地方吧。
局部內部類
仔細觀察示例代碼,TimePrinter只在start方法裏調用了,咱們能夠把這個類縮小爲局部內部類,即在方法內部聲明內部類。
局部內部類能夠訪問局部變量。
咱們將上述demo修改下:
public void start(int interval, boolean beep) { class TimePrinter implements ActionListener { @Override public void actionPerformed(ActionEvent e) { System.out.println("Every "+interval+", At the tone, the time is " + new Date()); if (beep) { Toolkit.getDefaultToolkit().beep(); } } } ActionListener listener = new TimePrinter(); Timer timer = new Timer(interval, listener); timer.start(); }
局部類不能用public或private訪問說明符進行說明,它的做用被限定在聲明這個局部類的塊中。
上述demo,局部內部類TimePrinter會訪問局部變量interval和beep。同理,依舊是將這兩個變量轉換程內部類的成員變量,實現值的傳遞。
匿名內部類
public class AnonymousInnerClassTest { @Data static class Animal { private String name; public void sayHi(){ System.out.println("Hi " + name); } } public static void main(String[] args) { Animal animal = new Animal(){ @Override public void sayHi() { System.out.println("Wang"); } }; animal.sayHi(); } }
上述簡單模擬一種Animal匿名類。編譯後的結果爲:
'AnonymousInnerClassTest$1.class' 'AnonymousInnerClassTest$Animal.class' AnonymousInnerClassTest.class
編譯器生成了3個編譯類。
AnonymousInnerClassTest
public class AnonymousInnerClassTest { public AnonymousInnerClassTest() { } public static void main(String[] args) { com.test.java.clazz.innerclass.AnonymousInnerClassTest.Animal animal = new 1(); animal.sayHi(); } }
AnonymousInnerClassTest$1
import com.test.java.clazz.innerclass.AnonymousInnerClassTest.Animal; final class AnonymousInnerClassTest$1 extends Animal { AnonymousInnerClassTest$1() { } public void sayHi() { System.out.println("Wang"); } }
AnonymousInnerClassTest$Animal
class AnonymousInnerClassTest$Animal { private String name; public void sayHi() { System.out.println("Hi " + this.name); } public AnonymousInnerClassTest$Animal() { } //getter setter equals toString }
即編譯器生成一個包訪問權限的內部類AnonymousInnerClassTest$Animal, 對於其匿名子類,又生成了final的AnonymousInnerClassTest$1 ,若是還有第二個匿名類,則命名AnonymousInnerClassTest$2.
原文出處:https://www.cnblogs.com/woshimrf/p/java-inner-class.html