一 事件
1.1 事件源
图形用户界面上每个可能产生事件的组件称为事件源。
1.2 事件监听者
Java系统中注册的用于接收特殊事件的类。不同的事件对应着不同的监听者,要想事件被监听者监听并处理,则需先将事件源注册到监听者。
1.3 事件处理流程
事件源触发事件并将事件作为一个参数传递给监听者,监听者实现某个接口中的抽象方法,从而实现对事件的处理。Java的事件处理机制是一个委托事件模型。
事件源注册的方法如下:
public void addActionListener(ActionListener l)
添加特定的动作,监听接收来自事件源的动作事件,如果l为空,不会产生任何动作。
监听者实现的接口为ActionListener接口,接口ActionListener来自包java.awt.event。
在此接口中只有一个方法:
public void actionPerformed(ActionEvent e)
当事件对象e发生时,调用此方法。监听者就需要实现这个方法。
1.4 动作事件
ActionEvent包含一个事件,该事件为执行动作事件ACTION_PERFORMED。触发这个事件的动作为:
(1) 点击按钮。
(2) 双击列表中的选项。
(3) 选择菜单项。
(4) 在文本框中输入回车。
常用方法如下:
public String getActionCommand() 返回引发某个事件的命令按钮的名字,如果名字为空,那么返回标签值。
public void setActionCommand(String command) 设置引发事件的按钮的名字,默认设置为按钮的标签。
例:测试动作事件

1 package test; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import java.applet.*; 6 public class UseButton extends Applet implements ActionListener{ 7 /** 8 * 9 */ 10 private static final long serialVersionUID = 1L; 11 12 String str = new String(); 13 Button b1; //声明按钮对象; 14 Button b2; 15 Color c; 16 public void init() 17 { 18 b1 = new Button("按钮对象1"); 19 b2 = new Button("按钮对象2"); 20 //添加事件监听者 21 b1.addActionListener(this); 22 b1.setBackground(Color.yellow); 23 b2.addActionListener(this); 24 this.add(b1); 25 this.add(b2); 26 27 } 28 public void start() 29 { 30 str = b1.getLabel(); 31 //repaint(); 32 } 33 public void paint(Graphics g) 34 { 35 g.setColor(c); 36 g.drawString("引发事件的对象的标签:" + str, 80,60); 37 } 38 //实现接口中的方法,响应动作事件 39 public void actionPerformed(ActionEvent e) 40 { 41 String arg = e.getActionCommand(); 42 if(arg == "按钮对象1") 43 { 44 c = Color.red; 45 str = "按钮对象1"; 46 } 47 else if(arg == "按钮对象2") 48 { 49 c = Color.green; 50 str = "按钮对象2"; 51 } 52 repaint(); 53 } 54 }
点击按钮对象1 ,点击按钮对象2
输出结果:
1.5 文本事件(TextEvent)
文本事件即代表文本区域中文本变化的事件TEXT_VALUE_CHANGED,在文本区域中改变文本内容。
public void addTextListener(TextListener l) 添加特定的文本事件,监听者接收来自文本对象的文本事件。如果l为空,那么不会抛出任何异常,而且也不会完成任何动作。
public interface TextListener extends EventListener 用于接收文本事件的监听者接口。当对象的文本发生变化时,调用监听者对象的方法。
接口中的方法为:
public void textValueChanged(TextEvent e) 当文本发生改变时调用。
public Object getSource() 发生事件的对象,从EventObject继承来的方法。
例:测试文本事件

1 package test; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import java.applet.Applet; 6 public class UseTextEvent extends Applet implements ActionListener, TextListener{ 7 /** 8 * @ YMM 2016/05/09 9 */ 10 private static final long serialVersionUID = 1L; 11 TextField tOld; 12 TextArea tNew; 13 Panel p; 14 public void init() 15 { 16 tOld = new TextField(25); 17 tNew = new TextArea("",8,25,TextArea.SCROLLBARS_NONE);; 18 //添加事件监听者 19 tOld.addActionListener(this); 20 tOld.addTextListener(this); 21 //设置界面 22 p = new Panel(new BorderLayout()); 23 24 p.add(tOld,BorderLayout.NORTH); 25 p.add(tNew,BorderLayout.SOUTH); 26 27 add(p); 28 } 29 //响应文本事件 30 public void textValueChanged(TextEvent e) 31 { 32 if(e.getSource() == tOld) 33 tNew.setText(tOld.getText()); 34 } 35 //响应动作事件 36 public void actionPerformed(ActionEvent e) 37 { 38 if(e.getSource() == tOld) 39 tNew.setText(""); 40 } 41 };
1.6 选择事件(ItemEvent)
选择事件中包含以事件为代表的选择项,选中状态发生变化的事件ITEM_STATE_ CHANGED。引发的动作为:
(1) 改变列表类List对象选项的选中或不选中状态。
(2) 改变下拉列表类Choice对象选项的选中或不选中状态。
(3) 改变复选按钮类Checkbox对象的选中或不选中状态。
事件源对象注册的方法如下:
public void addItemListener(ItemListener l) 添加特定的项监听者,接收对象的选择项发生变化的事件。
public ItemSelectable getItemSelectable() ItemEvent事件的方法,返回产生事件的事件源对象。
public interface ItemListener extends EventListener 接收选项事件的监听者接口。当选项中事件发生时,调用监听对象的itemStateChanged方法。
public void itemStateChanged(ItemEvent e) 当用户选中一项或未选中一项时,调用这个方法。

1 package test; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.applet.*; 5 6 7 public class UseItemEvent extends Applet implements ItemListener{ 8 /** 9 * 2016/05/09 @author jftt 10 */ 11 private static final long serialVersionUID = 1L; 12 Checkbox cDisp; //复选按钮 13 Button btnDisp; // 14 Choice cFont; //下拉列表 15 public void init() 16 { 17 cDisp = new Checkbox("红色"); 18 btnDisp = new Button("颜色显示"); 19 //setLayout(null); 20 btnDisp.setSize(120,120); 21 cFont = new Choice(); 22 cFont.add("10"); 23 cFont.add("12"); 24 cFont.add("14"); 25 //添加事件 26 cDisp.addItemListener(this); 27 cFont.addItemListener(this); 28 add(cDisp); 29 add(cFont); 30 add(btnDisp); 31 } 32 //接口事件 33 public void itemStateChanged(ItemEvent e) 34 { 35 Checkbox temp; 36 Choice temp2; 37 Font oldF; 38 //复选框 39 if(e.getItemSelectable() instanceof Checkbox) 40 { 41 temp = (Checkbox)(e.getItemSelectable()); 42 //选中为红色,否则为蓝色 43 if(temp.getState()) 44 btnDisp.setBackground(Color.red); 45 else 46 btnDisp.setBackground(Color.yellow); 47 } 48 //组合框 49 if(e.getItemSelectable() instanceof Choice) 50 { 51 oldF = btnDisp.getFont(); 52 temp2 = (Choice)(e.getItemSelectable()); 53 String s = temp2.getSelectedItem(); 54 String s1=oldF.getName(); 55 int s2=oldF.getStyle(); 56 //设置字体 57 btnDisp.setFont(new Font(s1,s2,Integer.parseInt(s)));// 字体,样式(粗体,斜体等),字号 58 } 59 } 60 61 }
1.7 调整事件(AdjustmentEvent)
调整事件包含一个事件,即ADJUSTMENT_VALUE_CHANGED事件,当操纵滚动条改变其滑块位置时引发动作。AjustEvent的方法如下:
public Adjustable getAdjustable() 返回引发事件的对象。
public int getValue() 返回调整事件中的当前值。
public void addAdjustmentListener(AdjustmentListener l) 添加调整监听者来接收来自对象的AdjustmentEvent实例。
public interface AdjustmentListener extends EventListener 接收调整事件的监听接口,有一个方法:
public void adjustmentValueChanged(AdjustmentEvent e) 可在调整改变时调用这个值。
例:测试调整事件。设置一个水平滚动条,取值为1~36,随着滑块的变化,滚动条的值将显示在文本区域中,并且字体大小也会跟随变化

1 package test; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.applet.*; 5 6 public class UseAdjustmentEvent extends Applet implements AdjustmentListener{ 7 /** 8 * @author YMM 2016/05/09 9 */ 10 private static final long serialVersionUID = 1L; 11 Scrollbar s; 12 TextArea txtValue; 13 Panel p; 14 public void init() { 15 s = new Scrollbar(Scrollbar.HORIZONTAL,0,1,10,36); 16 //添加监听者 17 s.addAdjustmentListener(this); 18 txtValue = new TextArea(5,25); 19 //界面布局 20 p = new Panel(new BorderLayout()); 21 p.add(s,BorderLayout.NORTH); 22 p.add(txtValue,BorderLayout.SOUTH); 23 add(p); 24 } 25 public void adjustmentValueChanged(AdjustmentEvent e) { 26 int value; 27 Font oldF; 28 if(e.getAdjustable() == s) 29 { 30 //得到滚动条的值 31 value = e.getValue(); 32 //将值写入文本区域 33 txtValue.setText(new Integer(value).toString()); 34 //按照滚动条的值设置字体 35 oldF = txtValue.getFont(); 36 txtValue.setFont(new Font(oldF.getName(),oldF.getStyle(),value)); 37 } 38 } 39 }
1.8 鼠标事件(MouseEvent)
表明画布或界面组件中发生的鼠标事件,包含按下鼠标、释放鼠标、单击鼠标、进入部件的地理位置的鼠标事件和退出部件的地理位置的鼠标事件,以及鼠标移动事件(鼠标移动和鼠标拖动)。
鼠标使用addMouseListener方法注册,通过MouseListener接收鼠标事件;鼠标还可以使用addMouseMotionListener方法注册,通过MouseMotionListener监听者监听鼠标移动事件。
监听者中有具体的方法分别针对上述具体的鼠标事件,系统能够自动分辨鼠标事件的类型并调用相应的方法,所以只需编码实现相应的代码就可以了。
public int getButton() 返回哪个按钮发生变化。
public int getClickCount() 返回与这个事件相关的鼠标单击的次数。
public Point getPoint() 返回同源部件相对的事件发生的x、y位置。
public int getX() 返回同源部件相对的事件发生的x位置。
public int getY() 返回同源部件相对的事件发生的y位置。
例:测试按钮和画布的鼠标事件,包括单击、按下、进入和退出等

1 package test; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.applet.*; 5 6 public class UseMouseEvent extends Applet implements MouseListener,MouseMotionListener{ 7 /** 8 * 9 */ 10 private static final long serialVersionUID = 1L; 11 Button btn; 12 public void init() 13 { 14 btn = new Button("演示鼠标事件"); 15 add(btn); 16 //给按钮添加鼠标事件和鼠标移动事件 17 btn.addMouseListener(this); 18 btn.addMouseMotionListener(this); 19 //给画布添加鼠标事件和鼠标移动事件 20 this.addMouseListener(this); 21 this.addMouseMotionListener(this); 22 } 23 //单击事件 24 public void mouseClicked(MouseEvent e) 25 { 26 Point p = new Point(); 27 if(e.getSource() == btn)//getSource()发生事件的对象,从EventObject继承来的方法。 28 29 { 30 if(e.getClickCount() == 1) 31 { 32 btn.setLabel("单击鼠标"); 33 } 34 else if(e.getClickCount() == 2) 35 { 36 btn.setLabel("双击鼠标"); 37 } 38 } 39 else 40 { 41 if(e.getClickCount() == 1) 42 { 43 p = e.getPoint(); 44 showStatus(p.x + "," + p.y + "单击鼠标"); 45 } 46 else if(e.getClickCount() == 2) 47 { 48 p = e.getPoint(); 49 showStatus(p.x + "," + p.y + "双击鼠标"); 50 } 51 } 52 } 53 //进入事件 54 public void mouseEntered(MouseEvent e) 55 { 56 if(e.getSource() == btn) 57 btn.setLabel("进入Button"); 58 else 59 showStatus("进入Applet"); 60 } 61 public void mouseExited(MouseEvent e) 62 { 63 if(e.getSource() == btn) 64 btn.setLabel("退出Button"); 65 else 66 showStatus("退出Applet"); 67 } 68 //按下事件 69 public void mousePressed(MouseEvent e) 70 { 71 if(e.getSource() == btn) 72 btn.setLabel("按下鼠标"); 73 else 74 showStatus("按下鼠标"); 75 } 76 //释放事件 77 public void mouseReleased(MouseEvent e) 78 { 79 if(e.getSource() == btn) 80 btn.setLabel("松开鼠标"); 81 else 82 showStatus("松开鼠标"); 83 } 84 //移动事件 85 public void mouseMoved(MouseEvent e) 86 { 87 if(e.getSource() == btn) 88 btn.setLabel("移动鼠标"); 89 else 90 showStatus("移动鼠标,新位置" + e.getX() + "," + e.getY()); 91 } 92 //拖动事件 93 public void mouseDragged(MouseEvent e) 94 { 95 if(e.getSource() == btn) 96 btn.setLabel("拖动鼠标"); 97 else 98 showStatus("拖动鼠标"); 99 } 100 }
1.9 键盘事件(KeyEvent)
键盘事件有三个:键盘按键按下,按键释放,按键被敲击。常用方法如下:
public char getKeyChar() 返回事件中键的字符。
public int getKeyCode() 返回整数键码。
public static String getKeyText(int keyCode) 返回描述这个键码的字符串,例如“HOME”、“F1”或者“A”等。
public interface KeyListener extends EventListener 用来接收键盘事件。使用方法addKeyListener注册。
针对键盘的三个事件接口提供相应的方法进行处理,具体方法如下:
public void keyPressed(KeyEvent e) 按键时引发事件处理。
public void keyReleased(KeyEvent e) 释放键时引发事件处理。
public void keyTyped(KeyEvent e) 键入键时引发事件处理。
例:

1 package test; 2 3 import java.applet.Applet; 4 import java.awt.*; 5 import java.awt.RenderingHints.Key; 6 import java.awt.event.ActionEvent; 7 import java.awt.event.ActionListener; 8 import java.awt.event.KeyEvent; 9 import java.awt.event.KeyListener; 10 import java.awt.event.TextEvent; 11 import java.awt.event.TextListener; 12 13 14 public class UseKeyEvent extends Applet implements KeyListener,ActionListener, TextListener { 15 /** 16 * 17 */ 18 private static final long serialVersionUID = 1L; 19 Key key; 20 Button btn; 21 TextField txt; 22 public void init() 23 { 24 btn = new Button("演示键盘事件"); 25 add(btn); 26 27 btn.addKeyListener(this); 28 29 this.addKeyListener(this); 30 txt = new TextField(25); 31 //添加事件监听者 32 txt.addActionListener(this); 33 txt.addTextListener(this); 34 //设置界面 35 36 37 add(txt,BorderLayout.NORTH); 38 39 } 40 @Override 41 public void keyTyped(KeyEvent e) {//键入键时引发事件处理 42 // TODO 自动生成的方法存根 43 char ch = e.getKeyChar(); 44 if(ch =='Y' || ch == 'y') 45 txt.setText ("同意"); 46 else if (ch == 'N' || ch == 'n') 47 txt.setText ("反对"); 48 else 49 txt.setText ("无效"); 50 51 } 52 @Override 53 public void keyPressed(KeyEvent e) { 54 // TODO 自动生成的方法存根 55 btn.setLabel("按键时引发事件处理"); 56 } 57 @Override 58 public void keyReleased(KeyEvent e) { 59 // TODO 自动生成的方法存根 60 btn.setLabel("释放键时引发事件处理"); 61 } 62 @Override 63 public void textValueChanged(TextEvent e) { 64 // TODO 自动生成的方法存根 65 66 } 67 @Override 68 public void actionPerformed(ActionEvent e) { 69 // TODO 自动生成的方法存根 70 71 } 72 }
二 多 线 程 机 制
2.1 线程简介
线程(thread)就是进程中的一个执行线索。Java虚拟机允许进程中同时执行多个线程。每个线程都有一个优先级。具有较高优先级的线程先执行。
线程是操作系统分配 CPU 时间的基本实体。每一个应用程序至少有一个线程,也可以拥有多个线程。线程是程序中的代码流。多个线程可以同时运行并能共享资源。
线程与进程不同,每个进程都需要操作系统为其分配独立的地址空间,而同一进程中的各个线程是在同一块地址空间中工作。
在 Java 程序中,一些动态效果(如动画的实现、动态的字幕等)常利用多线程技术来实现。
线程存在一个生命周期,由以下方法体现:
(1) start()方法:启动一个线程。
(2) run()方法:定义该线程的动作。
(3) sleep()方法:使线程睡眠一段时间,单位为ms。
(4) suspend()方法:使线程挂起。
(5) resume()方法:恢复挂起的线程。
(6) yield()方法:把线程移到队列的尾部。
(7) stop()方法:结束线程生命周期并执行清理工作。
(8) destroy()方法:结束线程生命周期但不做清理工作。
其中最常用的是方法start()、run()、sleep()、stop()。
2.2 线程类和Runnable接口
2.2.1. 建立Thread类的子类
class myThread extends Thread
{
...
public void start()//启动线程
{
...
}
public void run()//运行线程
{
...
}
}
例:多线程实例,主函数给予调用

1 public class MThread 2 { 3 public static void main(String[] args) 4 { 5 System.out.println("Hello World!"); 6 thread2 t1 = new thread2("线程实例1"); //创建线程实例 7 t1.start(); //调用 8 thread2 t2 = new thread2("线程实例2"); 9 t2.start(); 10 thread2 t3 = new thread2("线程实例3"); 11 t3.start(); 12 } 13 } 14 //自定义线程类thread2 15 class thread2 extends Thread 16 { 17 Thread thread; //定义线程实例 18 String str; 19 //构造函数 20 public thread2(String str) 21 { 22 this.str = str; 23 } 24 //启动线程 25 public void start() 26 { 27 thread = new Thread(this); 28 thread.start(); 29 } 30 public void run() 31 { 32 int i = 0; 33 while(thread != null) 34 { 35 try 36 { 37 //计数到5时睡眠10秒 38 if(i == 5) 39 sleep(10000); 40 } 41 catch(Exception e) 42 { 43 System.out.println(e.getMessage()); 44 } 45 System.out.println(str); 46 i++; 47 } 48 } 49 };
2.2.2. 实现接口Runnable
public interface Runnable
Runnable接口可以由任意试图实现线程机制的类来实现。接口包含一个run方法。
public void run()
对象实现Runnable接口时,创建一个线程,启动线程导致对象run方法的调用。
实现接口Runnable进行多线程设计的方法较为常用。下面给出一个例子。
例:编写Applet,实现Runnable接口进行简单的动画演示:三幅图片依次上移

1 package test; 2 3 import java.applet.Applet; 4 import java.awt.Graphics; 5 import java.awt.Image; 6 import java.awt.Toolkit; 7 8 import javax.print.DocFlavor.URL; 9 10 11 public class UseRunnable extends Applet implements Runnable{ 12 /** 13 * 14 */ 15 private static final long serialVersionUID = 1L; 16 Thread t; 17 Image imgs[]; 18 int high,h1,h2,h3; 19 public void init() 20 { 21 high = getHeight()/3; 22 h1 = high; 23 h2 = high * 2; 24 h3 = high * 3; 25 imgs = new Image[3]; 26 for (int i = 0; i < 3; i ++) 27 { 28 /*java.net.URL url=getDocumentBase(); 29 imgs[i] = getImage(getDocumentBase(),(i+1) + ".jpg");*/ 30 imgs[i]=Toolkit.getDefaultToolkit().getImage("E:\\WorkSpace\\ecplise-luna\\JavaTest\\src\\test\\"+(i+1)+".jpg "); 31 } 32 } 33 public void start() 34 { 35 t = new Thread(this); 36 t.start(); 37 } 38 public void stop() 39 { 40 t = null; 41 } 42 //实现接口的run方法,获得动画效果 43 public void run() 44 { 45 while( t != null) 46 { 47 try 48 { 49 Thread.sleep(100); 50 repaint(); 51 h1 --; 52 //上移,到顶点时睡眠 53 if(h1 == 0) 54 { 55 Thread.sleep(3000); 56 h2 = high; 57 } 58 //上移,到顶点时睡眠 59 h2 --; 60 if(h2 == 0) 61 { 62 Thread.sleep(3000); 63 h3 = high; 64 } 65 //上移,到顶点时睡眠 66 h3--; 67 if(h3 == 0) 68 { 69 Thread.sleep(3000); 70 h1 = high; 71 } 72 }catch(InterruptedException e){ 73 System.out.println(e.getMessage()); 74 } 75 } 76 } 77 public void paint(Graphics g) 78 { 79 //三幅图片依次显示 80 g.drawImage(imgs[0],0,h1,this); 81 g.drawImage(imgs[1],0,h2,this); 82 g.drawImage(imgs[2],0,h3,this); 83 } 84 public void update(Graphics g) 85 { 86 paint(g); 87 } 88 }