带有匿名EventListener的JPanel – 为什么GC不会破坏侦听器?
我一直在仔细阅读JMapViewer的开源代码。 如果有其他人希望查看它,请检查SVN 。
简而言之,主类是JMapViewer
,它是JPanel
的扩展。 还有另一个非常重要的类,名为DefaultMapController
,它充当主类的MouseListener
。
我注意到的第一个奇怪的事情是观众没有对控制器的引用。 JMapViewer
构造函数实例化DefaultMapController
的匿名实例,如下所示:
public JMapViewer() { // other stuff new DefaultMapController(this); }
在我看来这是一个糟糕的设计选择,因为控制器有很多方法(选项,切换等 – 下面显示的例子),现在根本无法访问,所以有什么好处呢?
public void setMovementMouseButton(int movementMouseButton) { // changes which mouse button is used to move the map }
控制器确实有一个对查看器的引用,如上面的第一个片段所示,这是它能够控制的方式。
然而,那时我想到的东西更怪异! 如果这个监听器的匿名实例没有引用,为什么它甚至可以存活? GC不应该迅速摧毁它吗? 或者GC是否足够聪明,知道引用实时JComponent
的侦听器类也必须保持活动才能正常工作,即使它由于某些奇怪的原因而没有名称?
那么,两个真正的问题:
- 为什么GC不会破坏对象?
- 这确实是一个糟糕的设计选择,还是有一些方法我不知道从实例化查看器的类访问控制器?
我想为这个开源库做贡献,我改变的第一个想法是改变JMapViewer
类,让一个字段引用它的控制器,并改变构造函数,将当前的匿名控制器分配给这个新字段。 但是,我想确保我不会无知地遗漏某些东西。 我在整个代码库中搜索了DefaultMapController
文本,它只出现在它自己的类定义中,以及JMapViewer
构造函数中的匿名实例化中。
编辑:
确实有一种方法可以通过使用java.awt.Component
方法getMouseListeners()
来访问匿名侦听器。 因此,在我的应用程序中,我可以在此集合中搜索DefaultMapController
实例,并使用它来访问我需要用来更改控制器选项的方法。
但是,为了扮演魔鬼的拥护者,如果我采用原创的想法并给地图作为其控制器的参考,现在我有一种循环参考(地图知道控制器和控制器知道地图)。 这是一个坏主意吗?
抽象父JMapController
包含对DefaultMapController
构造函数传递的JMapViewer
的引用:
public DefaultMapController(JMapViewer map) { super(map); }
附录:控制器持有的map
参考用于(有选择地)向地图的EventListenerList
添加最多三个控制器参考, 这里讨论。 其中任何一个都会排除GC。 至少有一个有益的设计好处是具体的JMapController
只需要实现可用的接口。
正如本MVC大纲中所建议的那样,给视图提供对控制器的引用是不常见的。 相比之下,让控制器注册为视图的监听器没有任何问题,如此处所示 。
请注意,只有无参数的JMapViewer
构造函数会安装DefaultMapController
。 您可以使用备用构造函数,如Demo.java版本29113中第57-59行的注释中Demo.java
。 这里检查一个完整的例子。
1)你知道的一切是,如果VM认为合适,它将收集部分或全部死亡对象。 GC不需要做任何事情。
2)最好的事情是询问图书馆的维护者。 无论如何,作为一般规则,除非有充分的理由,否则我不会费心去改变任何事情,例如,如果它明显提高了可读性,而是宁愿专注于真正的问题。
3)不确定是否是这种情况,但是,当您序列化JComponent时,还会序列化其所有字段。 而且你不想序列化很多未使用的东西。