阿里架构师详解Java内部类是如何实现的
目录
区域性内部类匿名内部类内部类(inner class)是定义在另一个类中的类。
内部类方法可以访问该类定义所在的作用域中的资料,包括私有的资料。内部类可以对同一个包中的其他类隐藏起来当想定义一个回拨函式且不想编写大量程式码时,使用匿名(anonymous)内部类比较便捷。以下简单举例:

一个时钟需要两个引数:释出通告的时间间隔以及开关铃声的标志。
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语法的时候,是把内部类当做该类的一个属性去记忆和理解的,因此,自然可以访问私有变数。这个理解,现在看来是错误的。
内部类是一种编译器现象,与虚拟机器无关。编译器将会把内部类翻译成用$分隔外部类名与内部类名的常规类档案,而虚拟机器对此一无所知。
上述内部类被编译成TalkingClock$TimePrinter :
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);
}
可以发现,编译器给添加了新的静态方法 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");
}
}
AnonymousInnerClassTestAnimal‘‘‘classAnonymousInnerClassTestAnimal‘‘‘classAnonymousInnerClassTestAnimal {private String name;
public void sayHi() {
System.out.println("Hi " + this.name);
}
public AnonymousInnerClassTest$Animal() {
}
//getter setter equals toString
即编译器生成一个包访问许可权的内部类AnonymousInnerClassTestAnimal,对于其匿名子类,又生成了final的AnonymousInnerClassTestAnimal,对于其匿名子类,又生成了final的AnonymousInnerClassTest1 ,如果还有第二个匿名类,则命名AnonymousInnerClassTest$2.
写在最后
点关注,不迷路;持续更新Java相关技术及资讯文章
(此处已新增圈子卡片,请到今日头条客户端检视)欢迎加入头条圈子,一起学习进步。
(此处已新增圈子卡片,请到今日头条客户端检视)写出我的第一个框架:迷你版Spring MVC透彻理解MyBatis设计思想之手写实现透彻理解Spring事务设计思想之手写实现理解数据库连线池底层原理之手写实现手把手带你实现JDK动态代理手写实现一个迷你版的Tomcat自己动手写一个服务闸道器手把手带你设计一个百万级的讯息推送系统手把手带你秒杀架构实践(含完整程式码)解密Dubbo:自己动手编写一个较为完善的RPC框架(两万字干货)想要进阶架构师,不仅仅只是懂得框架原理。下面,就让我手把手带你手写Spring MVC、Spring、Mybatis、秒杀架构、RPC等框架,让你提升架构思维,真正吃透!