首頁  >  文章  >  Java  >  剖析Java中的事件處理與異常處理機制

剖析Java中的事件處理與異常處理機制

高洛峰
高洛峰原創
2017-01-22 16:50:561100瀏覽

一、事件處理
其實,由事件處理這個名字自然就想到MFC中的消息響應機制,就我的體會,它們應該算是南桔北枳的情形吧,我懷疑Java中的事件處理這個"新瓶"應是裝的MFC中的消息回應這個"舊酒"。
    所謂的"事件"即如鍵盤按鍵、滑鼠點擊等這類由動作或什麼導致某個狀態改變並需要對這個改變作相應響應的這類改變。我們可以將Java中的事件分為按鈕、滑鼠、鍵盤、視窗、其它事件這幾大類。
    事件處理模型
 1.   基於繼承的事件處理模型(JDK1.0)
JDK1.0中,事件處理是基於繼承的,事件先送到元件,然後沿著容器層級向上傳播。沒有被元件處理的事件會自動地繼續傳播到元件的容器。 ——這個與MFC中的原始事件回應順序或多態的回應機制似乎一致,而後面說的基於代理的事件處理機制似乎又與MFC的回呼機制一致。
    特定的處理方法
呼叫action()方法或handleEvent()方法來取得程式執行階段發生的事件,所有元件發生的事件都在此方法中處理。
    2、基於代理的事件處理模型(JDK1。1)
在這個模型中,事件被直接送往產生這個事件的組件,
對於每一個組件註冊一個或多個稱為監聽者的類,這些類包含事件處理器,用來接收和處理這個事件。
監聽者就是實作了Listener介面的類別。事件是只向註冊的監聽者報告的對象。每個事件都有一個對應的監聽者介面。
在Button物件上以滑鼠進行點擊時,將發送一個ActionEvent事件。這個ActionEvent事件會被使用addActionListener()方法進行註冊的所有ActionListener的actionPerformed()方法接收。
基於代理的事件處理模型的特徵
①事件不會被意外地處理。在層次模型中,一個事件可能會傳播到容器,並在非預期的層次被處理。
②有可能建立並使用適配器(adapter)類別對事件動作進行分類。
③有利於把工作分佈到各類別中。
重點學習這種事件處理模式 
3、事件
事件處理的三要素。
(1)事件來源  事件來源是一個事件的產生者,如按鈕、視窗及文字域等。
(2)事件類型  Java中所有的事件都封裝成一個類,這些事件類別被集中在java.awt.event包,所有的事件類別均繼承了AWTEvent類別和一個方法-getSouce()方法,該方法傳回發生事件的物件。
(3)事件監聽器  不同的型別事件發生後,由事件監聽器接收事件並呼叫對應的事件處理方法。所有的事件監聽器其實都是一個java.awt.event套件中的接口,引入了java.util.EventListener介面。不同事件類型的監聽器具有不同的方法。
事件處理步驟
① 程式加入java.awt.event套件:
Import java.awt.event;
② 給予所需的事件來源物件註冊事件監聽器:
事件來源物件.addXXXListener(XXXListener);
③ 實作相應的方法。如果某個監聽器介麵包含多個方法,則需要實作所有的方法 
例:b2.addActionListener(this)
4、事件Adapters(適配器) 
實作每個Listener介面的所有方法的工作量是非常大的,為了方便起見,Java語言提供了Adapters類,用來實作含有多個方法的類別。
你定義的Listener可以繼承Adapter類,而且只要重寫你所需要的方法。
例如,視窗事件對應的監聽器為WindowListener,它必須實作多個方法,包括windowOpened()、windowClosed()、windowClosing()、WindowIconfied()、WindowDeiconfied()、WindowActivated()、WindowDeactivated(),這樣加加大了不必要的程式設計工作量。
如果繼承了WindowAdapter,只需實作其中某一個或幾個方法,不需要實作所有的方法。後面很多的例子只實作windowClosing()一個方法,目的是在關閉視窗時退出系統。
4.1 按鈕事件的處理
    點選按鈕所發生的事件為動作事件
    動作事件對應的事件類別為ActionEvent類別 
      Performed(ActionEvent e)發生動作事件時被呼叫
    實現動作事件的操作工程:
        第一步,並註冊動作事件監聽器addActionListener(ActionListener)。
        第二步,實現ActionListener介面的方法:actionPerformed(ActionEvent e)  
4.2滑鼠事件的處理
觸發滑鼠事件的事件來源通常是一個容器,當滑鼠進入、離開容器,或是在容器中點選滑鼠、拖曳滑鼠等操作時,都發生滑鼠事件 
    滑鼠事件對應的事件類別是MouseEvent類別     中方法:
        getX()取得滑鼠的X座標
        getY()取得滑鼠的Y座標位置聆聽者對追蹤事件, MouseMotionListener(或MouseMotionAdapter)對應滑鼠移動事件。
    MouseListener(或MouseAdapter)的主要方法
        MousePressed(MouseEvent e)滑鼠按下時的處理方法
     Event e)滑鼠進入時的處理方法
        MouseExited(MouseEvent e)滑鼠離開時的處理方法
        MouseClicked(MouseEvent e)滑鼠點擊時的處理方法

     MouseMotionListener(或MouseMotionAdapter)的主要方法 
        MouseMoved(MouseEvent e)滑鼠移動時的處理方法
      在具有鍵盤焦點的組件中按下或釋放鍵盤等操作時,都會發生鍵盤事件 
    鍵盤事件對應的事件類別為KeyEvent類別 
        KeyEvent城市主要方法:
         getKeyText()取得按下或釋放的鍵的字串
    鍵盤事件對應的事件監聽器為:KeyListener或KeyAdapter 
    主要方法:
        Key 4. 4 視窗事件的處理
    有Window及其擴充類別(Frame、Dialog)等才能激發視窗事件,表示視窗處於啟動/無效狀態、圖示/非圖示狀態或開啟/關閉狀態等 
視窗事件對應的類別為WindowEvent ,監聽器為WindowListener(或WindowAdapter)
    主要方法:
        windowOpened(WindowEvent e)開啟視窗的事件處理
  Closing(WindowEvent e)正在關閉視窗的事件處理
        WindowActivated( WindowEvent e)啟動狀態的事件處理
        WindowDeactivated(WindowEvent e)無效狀態的事件處理
4.5 其它事件的處理
    4.5.      選項事件對應的事件監聽器為: ItemListener 
        方法:
            itemStateChanged (ItemEvent e)發生選項對應的事件類別為AdjustmentEvent類別:
        調整事件對應的事件監聽器為: AdjustmentListener
        方法:
            adjustmentValueChanged (AdjustmentEvent e)發生調整事件時被呼叫
        選項事件對應的事件監聽器為: ItemListener 
        方法:
       生下拉列表發生了動作時被調用
        (可見,下拉列表的事件處理與事件類型、事件監聽器及方法與復選框、單選框的事件處理的事件類型、事件監聽器及方法一樣)
4.5.4 選單事件的處理
        選單事件通常是當我們點選某個選單項目時所發生的事件。
        選單項目有兩種:
            MenuItem 動作事件
                   第一步,以所有的MenuItem選單項目註冊動作事件監聽器addActionListener(ActionListener)。
            第二步,並實現ActionListener介面的方法:actionPerformed(ActionEvent e)。在這個方法中以e.getSource()取得使用者所選的選單項,並進行對應的處理。
        CheckboxMenuItem的事件處理 
            的第一步,且給所有的CheckMenuItem選單項目註冊選項事件監聽器addItemListenerItemListenerener)。
            第二步,並實現ItemListener介面的方法:itemStateChanged(ItemEvent e)。在這個方法中以e.getSource()取得使用者所選的選單項,e.getItem()取得使用者所選的選單項目的標籤,e.getStateChange()取得是否選中,並進行對應的處理。

二、異常處理
任何好的程式語言和程式設計人員都不會忽略對異常的處理,作為比較熱門的物件導向程式設計的語言——Java,異常處理機制自然也是其重要特色之一。
    一般解釋異常,都將其說為:程式設計中的錯誤。但是,實際上這個錯誤可是非常頻繁,有多種,如:編譯錯誤、運行錯誤(具體上又分為:系統運行錯誤和邏輯運行錯誤,這個什麼系統運行錯誤,自己倒很少將其算作是程式設計中的錯誤了,之前。每個異常類別代表了運行錯誤(注意:是運行錯誤)。異常類別中包含了該運行錯誤的資訊及處理錯誤的方法等內容。
    Java的異常處理機制:
    每當Java程式運作過程中發生一個可辨識的運作錯誤時,(即該錯誤有一個異常類別與之相對應時),系統都會產生一個對應的該異常類別的對象,(注意:叫做產生一個異常類別物件。)即產生一個異常。
    一旦一個異常物件產生了,系統中就一定要有相應的機制來處理它,確保不會產生死機、死循環或其他對操作系統的損害,從而保證了整個程序運行的安全性
    異常和異常類別:
    Error:由Java虛擬機產生並拋出,Java程式不做處理.
    Runtime Exception(被0除等系統錯誤,陣列下標超範圍):由系統偵測, 使用者的Java 程式可不做處理,系統將它們交給缺省的異常處理程序(注意:有缺省的異常處理).
    Exception(程式中的問題,可預測的): Java編譯器要求Java程式必須捕獲或聲明所有的非運行時異常
使用者自行產生異常
    Exception類別
    建構子:
    public Exception();
    public Exception(String  s);可以接受對應的資訊參數所傳入的資訊相符的資訊通常是指所描述的錯誤。 。
    Exception類別也從父親Throwable繼承了若干方法,常用的有:
    1)public String toString();
    toString()方法傳回描述目前Exception 類別資訊的字串。
    2)public void printStackTrace();
    printStackTrace()方法沒有回傳值,它的功能是完成一個列印操作,在目前的標準輸出(一般就是螢幕)上列印輸出目前例外物件的堆疊使用軌跡,也即程式先後呼叫執行了哪些物件或類別的哪些方法,使得運行過程中產生了這個例外物件。
    系統定義的運作異常
這些子類別有些是系統事先定義好並包含在Java類別庫中的,稱為系統定義的運作異常
    使用者自訂的異常
    對於某個應用程式所特有的運作錯誤,則是使用者自訂的異常
    對於某個應用程式所特有的運作錯誤,則需要程式設計人員根據程式的特殊邏輯在使用者程式裡自己創建使用者自訂的異常類別和異常物件 
使用者定義的異常通常採用Exception作為異常類別的父類 
    但是這裡有一個還未懂的問題:發生一個錯誤,系統怎麼知道是可辨識的?又是怎麼產生對應異常類別物件?異常類別物件怎麼就知道去用對應方法解決?每個處理對應異常的異常類別物件就只有一個異常處理方法? ————————————原來,由使用者自訂的異常,是透過語句throw才會拋出異常。
    建立使用者自訂異常時,一般需要完成以下的工作:
    1)宣告一個新的異常類別,使其以Exception類別或其他某個已經存在的系統異常類別或使用者異常為父類。
    2)為新的異常類別定義屬性和方法,或重載父類別的屬性和方法,使這些屬性和方法能夠體現該類別所對應的錯誤的資訊。
    異常的拋出
     Java程式在運作時如果引發了一個可以辨識的錯誤,就會產生一個與該錯誤相對應的異常類別的對象,把這個過程叫做異常的拋出,
實際是相應異常類物件的實例的拋出。
    根據異常類別的不同,拋出異常的方式有系統自動拋出和使用者拋出兩種:
    1、系統自動拋出
    所使用的系統定義的運作錯誤異常都是由系統自動地拋出
    所使用的系統定義的運作錯誤異常都是由系統自動地拋出
    所使用的系統定義的運作錯誤異常都是由系統自動地拋出
    所使用的系統定義的運作錯誤異常都是由系統自動地拋出
   @ 、使用者拋出🎜    使用者自訂的異常不可能依賴系統自動拋出,而必須由使用者用Java語句拋出,在Java語句中,throw語句用來明確地拋出一個「異常」 🎜    用throw語句拋出出的格式🎜回傳類型方法名稱(參數清單) throws 要拋出的例外類別名稱清單{
                    …                    …
         } 
     注意:
    } 
     注意:
    } 
    拋出異常;
    傾向於將throw語句放在if語句的if分支中,
            if(I>100)
      throw的語句的方法,應當在方法頭定義中增加以下的部分:
        throws 要拋出的例外類別名稱清單
        這樣做主要是為了通知欲調用此方法的上層方法,語句不只一個,則應該在方法頭throws中列出所有可能的異常 
    3)Java語言要求所有用throws關鍵字聲明的類別和用throw拋出的物件必須是Throwable類別或其子類別。如果你試圖拋出一個不是可拋出(Throwable)對象,Java編譯器將會報錯
    異常處理:
    主要考慮如何捕捉異常,捕捉異常後程式如何跳轉,以及如何寫異常處理語句
     try…catch…finally 區塊
    1)try
    在try語句的{ }中包含了可能會拋出一個或多個異常的一段程式碼
    這些程式碼實際上指定了它後面的catch區塊所能捕捉的異常的範圍。 
    Java程式執行到try區塊中的語句時如果產生了異常,就不再繼續執行該try區塊中其他的語句,而是直接進入catch區塊中尋找第一個與之匹配的異常類型並進行處理。
    2)catch區塊
    catch語句的參數類似方法的定義,包括一個異常類型和一個異常物件。
    異常類型必須為Throwable類的子類,它指明了catch語句所處理的異常類型;
    異常對象則由Java運行時系統在try所指定的程序代碼塊中拋出的大括號中包含異常對象的處理的方法代碼。
    catch語句可以有多個,分別處理不同類型的異常。
    Java執行時間系統從上到下分別對每個catch語句處理的異常類型進行偵測,直到找到與之相符的catch語句為止。
    這裡,類型匹配指catch中的異常類型與產生的異常物件的類型完全一致或是異常物件的父類,因此,catch語句的排序順序應該是從特殊到一般。 (考慮為什麼?)
    3)finally區塊
    finally語句可以說是為異常處理事件提供的一個清理機制,一般用來關閉文件或釋放其他系統資源 
    在try-catch-finally語句中可以沒有finally部分的語句。
    如果沒有finally部分,則當try指定的程式碼拋出一個異常時,其他的程式碼就不會被執行;
    如果存在finally部分,則不論try區塊中是否發生了異常,是否執行過catch部分的語句,都要執行finally部分的語句。
    可見,finally部分的語句為異常處理提供了一個統一的出口。
    多重異常處理    
    一個try區塊可能會產生多種不同的異常,如果希望能採取不同的方法來處理這些例外,就需要使用多重異常處理機制。
多異常處理是透過在一個try區塊後面定義若干個catch區塊來實現的,每個catch區塊用來接收和處理一種特定的異常物件 
透過catch區塊的參數來判斷一個異常物件是否應為本catch區塊接收和處理的異常。
    被哪個catch區塊取得,根據異常物件與catch區塊的異常參數的匹配:當它們滿足下列三個條件的任何一個時,認為異常物件和參數相符:
    1)物件與參數屬於相同的例外類。
    2)異常物件屬於參數例外類別的子類別。
    3)異常物件實現了參數所定義的介面。
    如果try區塊產生的異常物件被第一個catch區塊所接收,則程式的流程將直接跳到這個catch語句區塊中,語句區塊執行完後就退出目前方法,try區塊中尚未執行的語句和其他的catch區塊將被忽略
如果try區塊產生的異常物件與第一個catch區塊不匹配,系統將自動轉到第二個catch區塊進行匹配,如果第二個仍不匹配,就轉向第三個、第四個…直到找到一個可以接收該異常物件的catch區塊,完成流程的跳躍。
    如果try區塊產生的異常物件被第一個catch區塊所接收,則程式的流程將直接跳到這個catch語句區塊中,語句區塊執行完後就退出目前方法,try區塊中尚未執行的語句和其他的catch區塊將忽略
    如果try區塊產生的異常物件與第一個catch區塊不匹配,系統將自動轉到第二個catch區塊進行匹配,如果第二個仍不匹配,就轉向第三個、第四個…直到找到一個可以接收該異常物件的catch區塊,完成流程的跳躍。
    若try區塊中所有語句的執行都沒有引發異常,則所有的catch區塊都會被忽略而不執行。
    注意:
    1)catch區塊中的語句應依照異常的不同執行不同的操作
    所以在處理多重異常時應注意認真設計各catch區塊的排列順序。一般地處理較具體和較常見的異常的catch塊應放在前面,而可以與多種異常相匹配的catch塊應放在較後的位置。

/*尝试用户运行错误的异常处理: 
 问题是这样的,当输入的用户工资初值少于800则是错误的,当然工资的变化如果超过20%,则也是错误的 
*/
import java.awt.*; 
import java.applet.*; 
import java.awt.event.*; 
public class UserExceptionApplet extends Applet implements ActionListener{ 
 Label prompt1=new Label("请输入雇员姓名和工资初值:"); 
 Label prompt2=new Label("请输入欲修改的工资"); 
 TextField name,isal,nsal; 
 String msg; 
 Employee Emp; 
 Button okBtn=new Button("OK"); 
 Button cancelBtn=new Button("Cancel"); 
 public void init(){ 
  name=new TextField(5); 
  isal=new TextField(5); 
  nsal=new TextField(5); 
  add(prompt1); 
  add(name); 
  add(isal); 
  add(prompt2); 
  add(nsal); 
  add(okBtn); 
  okBtn.addActionListener(this); 
  cancelBtn.addActionListener(this); 
  add(cancelBtn); 
 } 
 public void paint(Graphics g){ 
  g.drawString(msg,0,80); 
 } 
 public void CreateEmp(String empName,double sa){ 
  try{ 
   Emp=new Employee(empName,sa); 
   msg=new String(Emp.toString()); 
  } 
  catch(IllegalSalaryException ise){ 
   msg=new String(ise.toString()); 
  } 
 } 
 public void ChangeEmpSal(double changeSal){ 
  try{ 
   Emp.setEmpSalary(changeSal); 
   msg=new String(Emp.toString()); 
  } 
  catch(IllegalSalaryException illSal){ 
   msg=new String(illSal.toString()); 
  } 
  catch(IllegalSalaryChangeException illSalChange){ 
   msg=new String(Emp.toString()+illSalChange.toString()); 
  } 
 } 
 public void actionPerformed(ActionEvent e){ 
  String empName; 
  double empSal,changeSal; 
  Object obj=e.getSource(); 
  if(obj==okBtn){ 
   empName=new String(name.getText()); 
   if(empName==null){ 
    msg=new String("请先输入雇员姓名工资并创建之");  
   } 
   if(nsal.getText()==null){ 
    empSal=Double.valueOf(isal.getText()).doubleValue(); 
    CreateEmp(empName,empSal); 
   } 
   else{ 
    changeSal=Double.valueOf(nsal.getText()).doubleValue(); 
    ChangeEmpSal(changeSal); 
   } 
  } 
  if(obj==cancelBtn){ 
   naem.setText(""); 
   isal.setText(""); 
   nsal.setText(""); 
  } 
  repaint(); 
 }  
} 
  
class Employee{ 
 String m_EmpName; 
 double m_EmpSalary; 
   
 Employee(String name,double initsalary)throws IllegalSalaryException{ 
  m_EmpName=name;//看这里有问题没,参考代码为m_EmpName=new String(name); 
  if(initsalary<800){ 
   throw(new IllegalSalaryException(this,initsalary));//throw语句 
  } 
  m_EmpSalary=initsalary; 
 } 
 public String getEmpName(){ 
  return m_EmpName; 
 } 
 public double getEmpSalary(){ 
  return m_EmpSalary; 
 } 
   
 public boolean setEmpSalary(double newSal) throws IllegalSalaryException,IllegalSalaryChangeException{ 
  if(newSal<800) 
   throw(new IllegalSalaryException(this,newSal)); 
  else if(getEmpSalary()==0.0){ 
   m_EmpSalary=newSal; 
   return true; 
  } 
  else if(Math.abs(newSal-getEmpSalary())/getEmpSalary()>=0.2) 
   throw(new IllegalSalaryChangeException(this,newSal-getEmpSalary()));  
  else{ 
   m_EmpSalary=newSal; 
   return true; 
  } 
 } 
 public String toString(){ 
  String s; 
  s="姓名:"+m_EmpName+"工资: "+m_EmpSalary; 
  return s; 
 } 
} 
  
class IllegalSalaryException extends Exception{ 
 private Employee m_ConcernedEmp; 
 private double m_IllegalSalary; 
  
 IllegalSalaryException(Employee emp,double isal){ 
  super("工资低于最低工资"); 
  m_ConcernedEmp=emp; 
  m_IllegalSalary=isal; 
 } 
 public String toString(){ 
  String s; 
  s="为雇员提供的工资不合法:雇员:"+m_ConcernedEmp.getEmpName()+"非法工资:"+m_IllegalSalary+"低于最低工资数额800元"; 
  return s; 
 } 
} 
  
class IllegalSalaryChangeException extends Exception{ 
 private Employee m_ConcernedEmp; 
 private double m_IllegalSalaryChange; 
  
 IllegalSalaryChangeException(Employee emp,double csal){ 
  super("工资变动太大"); 
  m_ConcernedEmp=emp; 
  m_IllegalSalaryChange=csal; 
 } 
 public String toString(){ 
  String s; 
  s="为雇员提供的工资变动不合法:雇员:"+m_ConcernedEmp.getEmpName()+"非法变动工资变化:"+m_IllegalSalaryChange+"高于原工资的20%"; 
  return s; 
 } 
}

更多剖析Java中的事件處理與異常處理機制相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn