我是Java Programming的新手,但他是一位经验丰富的C ++程序员。我正在学习如何使用swing编程GUI。我想知道ActionListeners的资源密集程度(运行时和内存)是多少?是否应该在特定程序中创建的侦听器总数的一般准则?在性能受到影响之前会有多少?
我目前正在通过Deitel Developer Series Java for Programmers一书中学习Java。在一个特定的例子中,他们有一个JRadioButtonItems数组作为该类的私有变量。他们还创建了一个ItemHandler类,该类从ActionListener类扩展,该类对整个单选按钮阵列进行线性搜索,以确定所选的那个并相应地更改程序的状态。数组中的所有单选按钮共享相同的Action Listener。这对于进行信息的线性搜索似乎相当低效,所以我重写了ActionListener类以接受在构造函数中修改的建议值,并为每个单选按钮提供了自己的ActionListener,其中包含构造函数传递的建议值,以避免做一个线性搜索。哪种方法性能更好?我为听起来像菜鸟道歉,我只是想在Java中编写一套良好的编程习惯。附件是代码的一个小例子。感谢。
/************************************************************************
Original code in Deitel book with linear search of selected Radio button in Actionlistener
****************************************************************************/
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
public class MenuTest extends JFrame{
private final Color colorValues[] = {Color.BLACK, Color.WHITE, Color.GREEN};
private JRadioButtonMenuItem colorItems[];
private ButtonGroup colorButtonGroup;
public MenuTest(){
super("Menu Test");
JMenu fileMenu = new JMenu("File");
JMenuBar bar = new JMenuBar();
setJMenuBar(bar);
bar.add(fileMenu);
String colors[] = {"Black", "White", "Green"};
JMenu colorMenu = new JMenu("Color");
colorItems = new JRadioButtonMenuItem[colors.length];
colorButtonGroup = new ButtonGroup();
ItemHandler itemHandler = new ItemHandler();
for(int count = 0; count < colors.length; count++){
colorItems[count] = new JRadioButtonMenuItem(colors[count]);
colorMenu.add(colorItems[count]);
colorButtonGroup.add(colorItems[count]);
colorItems[count].addActionListener(itemHandler);
}
colorItems[0].setSelected(true);
fileMenu.add(colorMenu);
fileMenu.addSeparator();
}
private class ItemHandler implements ActionListener{
public void actionPerformed(ActionEvent event){
for(int count = 0; count < colorItems.length; count++){
if(colorItems[count].isSelected()){
getContentPane().setBackground(colorValues[count]);
}
}
}
}
public static void main(String args[]){
MenuTest menuFrame = new MenuTest();
menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
menuFrame.setSize(600,400);
menuFrame.setVisible(true);
menuFrame.getContentPane().setBackground(menuFrame.colorValues[0]);
}
}
/************************************************************************
My Code redefined version of Deitel's w/o linear search in ActionListener
************************************************************************/
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
public class MenuTest extends JFrame{
private final Color colorValues[] = {Color.BLACK, Color.WHITE, Color.GREEN};
private JRadioButtonMenuItem colorItems[];
private ButtonGroup colorButtonGroup;
public MenuTest(){
super("Menu Test");
JMenu fileMenu = new JMenu("File");
JMenuBar bar = new JMenuBar();
setJMenuBar(bar);
bar.add(fileMenu);
String colors[] = {"Black", "White", "Green"};
JMenu colorMenu = new JMenu("Color");
colorItems = new JRadioButtonMenuItem[colors.length];
colorButtonGroup = new ButtonGroup();
ItemHandler itemHandler = new ItemHandler();
for(int count = 0; count < colors.length; count++){
colorItems[count] = new JRadioButtonMenuItem(colors[count]);
colorMenu.add(colorItems[count]);
colorButtonGroup.add(colorItems[count]);
colorItems[count].addActionListener(new ItemHandler(colorValues[count]));
}
colorItems[0].setSelected(true);
fileMenu.add(colorMenu);
fileMenu.addSeparator();
}
private class ItemHandler implements ActionListener{
private Color setColor;
public ItemHandler(Color inColor){
super();
setColor = inColor;
}
public void actionPerformed(ActionEvent event){
getContentPane().setBackground(setColor);
repaint();
}
}
public static void main(String args[]){
MenuTest menuFrame = new MenuTest();
menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
menuFrame.setSize(600,400);
menuFrame.setVisible(true);
menuFrame.getContentPane().setBackground(menuFrame.colorValues[0]);
}
}
答案 0 :(得分:18)
CPU使用率:接近无。只有在他们收听的对象的状态发生变化时才会调用监听器。他们并没有真正“倾听”。他们正在监听的对象在需要时调用它们。 (注意:这是一个简化)
内存使用:主要是ActionListener没有状态。因此,总持久内存使用量是任何对象所需的最小值。在您的示例中,是状态,因为您有一个setColor字段。但内存使用率很低。
IMO,听众效率很高,您可以随意使用。
答案 1 :(得分:4)
与他们所附带的组件相比,听众的成本非常低。只有数百名听众不太可能对CPU或内存产生太大影响。但是,工作听众总的来说更重要。如果发现性能问题,可以使用CPU /内存分析器来识别和优化原因。这不是你需要先担心的事情。
答案 2 :(得分:4)
这里更大的问题是建立时间,并在较小程度上建立永久性发电记忆。
对于实际的对象大小,您可以进行包络影响分析。一个对象大约20个字节。假设你有10,000个,那是200K。在一台1GB的机器上,有0.02%的机器内存供听众使用 - 我不会在手机中担心这一点。
更大的图片是你加载了多少个类。我的实验(在Usenet上的某个地方)为每个相对较小的匿名内部类加载了大约2K的开销。其中10,000个是20MB。我们1GB机器的2%。对于广泛分发的软件,我担心这一点,但对于中型公司或部门中的几十台机器,我不担心。即便如此,获得实际可行且可维护的软件显然更为重要(显然,85%的软件开发成本都在维护中(对于某些定义而言)并且大多数项目都没有那么远。)
加载代码是一项相对昂贵的操作。虽然幸运的是我们现在有了罐而不是打开一个新的套接字来为每个类文件做一个HTTP 1.0请求(applet开始很慢 - 这是怎么发生的?)。但是,必须查找字符串,解析方法,验证字节码等。在我们承担编译器费用之前,它仍然被解释为1,500次迭代。
一旦你工作(它在生产之前就没有工作),可维护的软件,那么你可能想要考虑微调性能(尽管,相反,最好是单挑)。调整最慢的部分。对于GHz处理器,每次点击鼠标的响应速度都要快得多。慢速往往是打开新对话框等操作。
因此,对我们来说,我们希望减少设置时间,但在事件发生时我们可能效率很低。 50ms是我们应该瞄准的响应时间,而在1GHz机器上是20,000,000个周期(批次!)。因此,适当的高效策略是使用少数类型的监听器。使用单个侦听器类型(可能是实例)来侦听相当数量的模型。忽略事件参数(对于状态更改侦听器通常是个好主意)并运行检查需要完成的操作(记住我们有足够的时间)。
我觉得有必要在这里重复一遍。让你的代码变得更好。转到许多输入侦听器(和命令)。状态变化听众应该忽略他们的论点。
答案 3 :(得分:2)
我5年来一直是Swing程序员。根据我的经验,听众是如何在那里从来不是一个问题。当听众不再对事件感兴趣时,取消注册是非常重要的。
请记住,在GUI应用程序中,响应时间比内存更重要。 您可能对我的文章感兴趣:
Use Weak Listeners to avoid Memory Leaks
Know when your Object gets Garbage Collected
Long-Lived Models may cause Memory Leaks
答案 4 :(得分:1)
请记住,聆听与忙碌的等待不同。监听器通过某种代理方法在相关方列表中注册。在大多数Listener模式中,CPU时间浪费很少。
答案 5 :(得分:1)
Java ME中有一个传统,即使用较少的类,因为下载另一个类文件的开销很大,因此有时候您会获得尝试减少侦听器类型数量的Java代码,并在侦听器中使用条件逻辑。对于桌面Java应用程序大小并不是一个真正的问题,但有时您会看到它泄漏到该空间。
并非真正回答这个问题,但是一旦你走了那么远,你会发现你可以将MenuTest的大多数字段的范围缩小为构造函数中的方法变量。
我倾向于使用匿名侦听器,因为大多数时候它们太简单而无法命名。
显示的代码使用动作侦听器来检测菜单项是否“已被动作”。
在构造函数中,它设置所选的菜单项,然后在main方法中访问对象的colorValues
字段,并设置背景的值。显然,这在信息隐藏或封装行为方面都失败了。
如果您使用ChangeListener
来检查菜单项是否设置为选中,那么您没有两个独立的逻辑部分来维持与设置颜色相同的状态以响应对setSelected
的调用,您不必将colorValues
字段泄漏给调用者。
for ( int count = 0; count < colors.length; count++ ){
colorItems[count] = new JRadioButtonMenuItem(colors[count]);
colorMenu.add(colorItems[count]);
colorButtonGroup.add(colorItems[count]);
final Color color = colorValues[count];
colorItems[count].addChangeListener ( new ChangeListener() {
public void stateChanged ( ChangeEvent event ){
if ( ( ( AbstractButton ) event.getSource() ).isSelected () )
getContentPane().setBackground ( color );
}
});
}
colorItems[0].setSelected(true);
// no call to setBackgroundColour() in main()
您还可以使用第二个最终变量来保留colorItem
,并避免在event.getSource()
上投射。
还可以实现更改侦听器以侦听设置框架背景颜色的更改,如果使用setBackground()更改颜色,则选择适当的菜单项。
调用setBackground()会自动将面板标记为已损坏,因此之后无需再调用重绘。
答案 6 :(得分:0)
好吧,考虑一下最基本的监听器实现:你所做的只是将一个对象添加到监听器列表中,仅此而已。如果愿意,注册回调函数。所以执行这一行:
someObject.addListener(new someAnonymousListener{ ... });
基本上是:
someListenerList.add(new someAnonymousListener{ ... });
到目前为止,没有任何伤害没有犯规。该对象只是与所有其他已注册的侦听器一起位于列表中。
但是,请注意在对象上触发事件时会发生什么:
for (SomeListener l : someListenerList) {
l.doSomeAction();
}
让人惊讶。这是你应该小心的地方。确保你的听众没有做任何过于复杂的事情,或者你会看到一个瓶颈,这会在分析过程中出现。
底线:只是在对象上持有一个监听器几乎是免费的。只要确保你没有注册太多的监听器,并且每个监听器都要保持简单明了的行动。