内部类(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