Java中虚拟机与编译器如何区分同名变量引用的两个对象?
嘿,这个问题问得特别到位——很多刚接触Java对象模型的开发者都会在这里卡一下,我来给你拆解清楚:
首先,变量jb只是个「引用」,不是对象本身
Java里的对象是存在堆内存里的,而你定义的JButton jb只是个「引用变量」,它的作用就是保存堆中某个对象的内存地址,相当于一个指向对象的“指针”。
咱们一步步看你的代码:
- 第一行
JButton jb = new JButton("firstJB");:在堆里创建了第一个按钮对象,jb这个变量现在保存的是这个对象的地址。 - 你调用
contentPane.add(jb);:这时候contentPane会自己保存一份这个按钮对象的引用——相当于它把这个按钮“记下来”了,和jb变量没关系了。 - 第五行
jb = new JButton("secondJB");:这时候堆里又创建了第二个全新的按钮对象,jb变量的内容被覆盖,现在保存的是这个新对象的地址。但之前的第一个按钮对象并没有消失,因为contentPane还拿着它的引用,垃圾回收器不会把它删掉。 - 最后
contentPane.add(jb);:contentPane又把第二个按钮的引用保存下来,现在它手里握着两个独立按钮对象的引用。
所以本质是:一个变量可以先后指向不同的对象,但对象的生命周期不依赖于指向它的变量——只要还有地方(比如contentPane)拿着它的引用,它就会一直存在。
事件监听是和「对象」绑定,不是和「变量」绑定
你提到的addActionListener(this),是把当前类的实例(this)注册为按钮对象自己的监听器。也就是说:
- 第一个按钮对象会记住“我的监听器是这个
this实例” - 第二个按钮对象同样会记住“我的监听器也是这个
this实例”
当某个按钮被点击时,是这个按钮对象自己触发ActionEvent,并且会把自己作为事件的「源对象」(可以通过event.getSource()获取)传递给监听器。
所以你在actionPerformed方法里区分两个按钮,靠的是判断event.getSource()到底是哪个按钮对象,而不是看变量jb现在指向谁——因为jb只是个“地址标签”,早就被改了,但两个按钮对象本身是独立存在的,各自带着自己的身份信息。
举个生活化的例子:
你有个小本子(变量jb),第一次写了张三的住址,然后把张三带到公司(contentPane);之后你把小本子上的地址改成李四的,再把李四带到公司。现在公司里有张三和李四,你的小本子只写着李四的地址,但张三依然在公司里。当张三或李四给你发消息(触发事件),你能通过消息里的发件人(getSource())知道是谁发的,而不是看你小本子现在写的是谁。
内容的提问来源于stack exchange,提问作者TEB

