阿里架构师详解Java内部类是如何实现的

本文详细介绍了Java内部类的工作原理,包括局部内部类和匿名内部类的使用。通过编译后的源码分析,揭示了内部类如何访问外部类的私有成员,以及局部内部类如何实现值传递。同时,文章探讨了匿名内部类的编译结果,展示了其在实际代码中的应用。
摘要由CSDN通过智能技术生成

目录

  • 局部内部类
  • 匿名内部类

内部类(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程序员,我这边准备免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

资料领取方式:加入Java技术交流群963944895点击加入群聊,私信管理员即可免费领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值