2009-07-05 24 views
14

我是Java編程新手,但有經驗的C++程序員。我正在學習如何使用swing編程GUI。我想知道資源密集型(運行時以及內存)是否是ActionListeners?對於應該在特定程序中創建的聽衆總數是否有一般指導原則?有多少人受到影響?資源密集型是java中的聽衆嗎?

我目前正在通過Deitel開發人員系列Java for Programmers書籍學習Java。在一個特定的例子中,他們有一個JRadioButtonItems數組作爲這個類的一個私有變量。他們還創建了一個ItemHandler類,該類從ActionListener類擴展而來,該類對整個單選按鈕陣列進行線性搜索,以確定所選的那個並相應地更改程序的狀態。數組中的所有單選按鈕共享同一個Action Listener。這對於進行信息的線性搜索似乎相當低效,所以我重寫了ActionListener類,以便在構造函數中修改建議的值併爲每個單選按鈕分配給自己的ActionListener,並使用構造函數傳入的建議值以避免做一個線性搜索。哪種方法在性能方面更好?我很抱歉聽起來像一個noob,我只是想開發一套用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]); 
     } 
    } 

回答

17

CPU使用率:接近無。只有當監聽對象的狀態發生變化時纔會調用監聽器。他們並不真正「傾聽」。他們正在聽的對象在需要時調用它們。 (注意:這是一個簡化)

內存使用情況:大多數情況下ActionListener沒有狀態。所以對於任何對象來說,總的持久內存使用量是最小的。在你的例子中,狀態,因爲你有一個setColor字段。但內存使用率很低。

國際海事組織,聽衆是有效的,你可以儘可能多地使用你想要的。

4

與它們所附的組件相比,聽衆的成本非常低。僅僅擁有數百個聽衆就不太可能對CPU或內存產生太大的影響。但是,總的工作聽衆做得更重要。如果看到性能問題,可以使用CPU /內存分析器來確定並優化原因。它不是你應該需要擔心太多前期的事情。

1

請記住,傾聽與忙碌的等待不一樣。監聽者通過某種代理方法在相關方列表中註冊。大多數監聽器模式中CPU時間很少浪費。

1

Java ME中有一個傳統使用較少的類,因爲下載另一個類文件的開銷很大,所以您有時會得到嘗試並減少偵聽器類型數量的Java代碼,並在偵聽器中使用條件邏輯。對於桌面Java應用程序的大小並不是一個真正的問題,但你有時會發現它泄漏到這個空間。

不是真的回答了這個問題,但是一旦你走了那麼遠,你會發現你可以將MenuTest的大部分字段的範圍縮小爲構造函數中的方法變量。

我傾向於使用匿名聽衆,因爲大多數時間他們太簡單,以至於打擾命名。

顯示的代碼使用動作偵聽器來檢測菜單項是否「動作」。

在構造函數中設置選定的菜單項,然後在主要方法中訪問對象的colorValues字段,並設置背景的值。顯然這在信息隱藏或封裝行爲上都失敗了。

如果您使用ChangeListener s來檢查菜單項是否被設置爲選中狀態,那麼您沒有兩個單獨的邏輯部分來保持與響應調用的顏色設置相同的狀態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()會自動標記爲已損壞的面板,所以沒有必要調用重繪之後。

0

好,考慮最基本的監聽器實現:你正在做的基本上是添加對象到聽衆的名單,僅此而已。註冊一個回調函數,如果你願意的話。因此,該行執行:

someObject.addListener(new someAnonymousListener{ ... }); 

主要是:

someListenerList.add(new someAnonymousListener{ ... }); 

截至目前爲止,無傷害無臭。該對象就是與其他所有已註冊的監聽者坐在一起的列表中。

然而,通知時觸發事件的對象會發生什麼:

for (SomeListener l : someListenerList) { 
    l.doSomeAction(); 
} 

哎呀。這是你應該小心的地方。確保你的聽衆沒有做太複雜的事情,否則你會看到一個瓶頸,這將在分析過程中出現。

底線:簡單地把一個監聽器放在一個物體上幾乎是免費的。只要確保你沒有註冊太多的聽衆,並且他們中的每一個都在保持它的動作簡單和重要。

+0

你養好問題:聽衆一般應做的很少。如果他們需要做一些複雜的事情,他們應該產生一個新的線程,最好使用SwingWorker類,然後立即返回。 – 2009-07-07 07:54:54

4

這裏的更大的問題是建立時間,以及在較小程度上永久代內存。

對於實際的對象大小,你可以做一個信封影響分析一回。大約20個字節的一個對象。假設你有10,000個,那就是200K。在一臺1GB的機器上,你的機器內存佔用了聽衆的0.02% - 我不會爲手機擔心。

特大局觀是你有多少類加載。我的實驗(在Usenet上某處)爲每個相對較小的匿名內部類加載了大約2K的開銷。其中10,000個是20MB。我們1GB機器的2%。我擔心廣泛分發的軟件,而不是中型公司或部門的幾十臺機器。即便如此,讓真正有效且可維護的軟件(顯然,85%的軟件開發成本在維護中(對於某些定義而言),大多數項目從未達到這個目標)真的更重要。

獲取代碼加載是一個相對昂貴的操作。雖然幸運的是我們現在有了jar而不是打開一個新的套接字來爲每個類文件做一個HTTP 1.0請求(applets開始很慢 - 這是怎麼發生的?)。不過,必須查找字符串,解決方法,驗證字節碼等。在我們承擔編譯器費用之前,它仍然被解釋爲1,500次迭代。一旦你開始工作(它在生產中不工作),可維護的軟件,那麼你可能需要考慮微調性能(儘管相反,這是很好的選擇)。調整最慢的部分。使用GHz處理器時,每次點擊鼠標的響應速度往往不夠快。緩慢往往是諸如打開新對話框等操作。

因此,對於我們來說,我們希望減少設置時間,但是在事件發生時我們可能會非常低效。 50毫秒是我們應該瞄準的響應時間,而在1千兆赫的機器上,這是20,000,000週期(批次!)。所以一個適當的高性能策略是使用少數類型的監聽器。使用單個偵聽器類型(可能是實例)來偵聽相當數量的模型。忽略事件參數(通常是狀態改變監聽器的一個好主意),並通過檢查需要完成的事情來運行(記住我們有足夠的時間)。

我覺得有必要在這裏重複一下自己。讓你的代碼更好。去尋找許多輸入監聽器(和命令)。狀態改變監聽器應該忽略它們的參數。

+0

10,000個對象@ 20字節!= 20K(應該是200K)。 – 2010-02-04 11:54:17