Java—事件和多线程机制

时间:2022-05-04 06:55:10

一  事件

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)        设置引发事件的按钮的名字,默认设置为按钮的标签。

例:测试动作事件

 package test;

 import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class UseButton extends Applet implements ActionListener{
/**
*
*/
private static final long serialVersionUID = 1L; String str = new String();
Button b1; //声明按钮对象;
Button b2;
Color c;
public void init()
{
b1 = new Button("按钮对象1");
b2 = new Button("按钮对象2");
//添加事件监听者
b1.addActionListener(this);
b1.setBackground(Color.yellow);
b2.addActionListener(this);
this.add(b1);
this.add(b2); }
public void start()
{
str = b1.getLabel();
//repaint();
}
public void paint(Graphics g)
{
g.setColor(c);
g.drawString("引发事件的对象的标签:" + str, 80,60);
}
//实现接口中的方法,响应动作事件
public void actionPerformed(ActionEvent e)
{
String arg = e.getActionCommand();
if(arg == "按钮对象1")
{
c = Color.red;
str = "按钮对象1";
}
else if(arg == "按钮对象2")
{
c = Color.green;
str = "按钮对象2";
}
repaint();
}
}

点击按钮对象1 ,点击按钮对象2

输出结果:

Java—事件和多线程机制      Java—事件和多线程机制

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继承来的方法。

例:测试文本事件

 package test;

 import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
public class UseTextEvent extends Applet implements ActionListener, TextListener{
/**
* @ YMM 2016/05/09
*/
private static final long serialVersionUID = 1L;
TextField tOld;
TextArea tNew;
Panel p;
public void init()
{
tOld = new TextField(25);
tNew = new TextArea("",8,25,TextArea.SCROLLBARS_NONE);;
//添加事件监听者
tOld.addActionListener(this);
tOld.addTextListener(this);
//设置界面
p = new Panel(new BorderLayout()); p.add(tOld,BorderLayout.NORTH);
p.add(tNew,BorderLayout.SOUTH); add(p);
}
//响应文本事件
public void textValueChanged(TextEvent e)
{
if(e.getSource() == tOld)
tNew.setText(tOld.getText());
}
//响应动作事件
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == tOld)
tNew.setText("");
}
};

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)                当用户选中一项或未选中一项时,调用这个方法。

 package test;
import java.awt.*;
import java.awt.event.*;
import java.applet.*; public class UseItemEvent extends Applet implements ItemListener{
/**
* 2016/05/09 @author jftt
*/
private static final long serialVersionUID = 1L;
Checkbox cDisp; //复选按钮
Button btnDisp; //
Choice cFont; //下拉列表
public void init()
{
cDisp = new Checkbox("红色");
btnDisp = new Button("颜色显示");
//setLayout(null);
btnDisp.setSize(120,120);
cFont = new Choice();
cFont.add("10");
cFont.add("12");
cFont.add("14");
//添加事件
cDisp.addItemListener(this);
cFont.addItemListener(this);
add(cDisp);
add(cFont);
add(btnDisp);
}
//接口事件
public void itemStateChanged(ItemEvent e)
{
Checkbox temp;
Choice temp2;
Font oldF;
//复选框
if(e.getItemSelectable() instanceof Checkbox)
{
temp = (Checkbox)(e.getItemSelectable());
//选中为红色,否则为蓝色
if(temp.getState())
btnDisp.setBackground(Color.red);
else
btnDisp.setBackground(Color.yellow);
}
//组合框
if(e.getItemSelectable() instanceof Choice)
{
oldF = btnDisp.getFont();
temp2 = (Choice)(e.getItemSelectable());
String s = temp2.getSelectedItem();
String s1=oldF.getName();
int s2=oldF.getStyle();
//设置字体
btnDisp.setFont(new Font(s1,s2,Integer.parseInt(s)));// 字体,样式(粗体,斜体等),字号
}
} }

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,随着滑块的变化,滚动条的值将显示在文本区域中,并且字体大小也会跟随变化

 package test;
import java.awt.*;
import java.awt.event.*;
import java.applet.*; public class UseAdjustmentEvent extends Applet implements AdjustmentListener{
/**
* @author YMM 2016/05/09
*/
private static final long serialVersionUID = 1L;
Scrollbar s;
TextArea txtValue;
Panel p;
public void init() {
s = new Scrollbar(Scrollbar.HORIZONTAL,0,1,10,36);
//添加监听者
s.addAdjustmentListener(this);
txtValue = new TextArea(5,25);
//界面布局
p = new Panel(new BorderLayout());
p.add(s,BorderLayout.NORTH);
p.add(txtValue,BorderLayout.SOUTH);
add(p);
}
public void adjustmentValueChanged(AdjustmentEvent e) {
int value;
Font oldF;
if(e.getAdjustable() == s)
{
//得到滚动条的值
value = e.getValue();
//将值写入文本区域
txtValue.setText(new Integer(value).toString());
//按照滚动条的值设置字体
oldF = txtValue.getFont();
txtValue.setFont(new Font(oldF.getName(),oldF.getStyle(),value));
}
}
}

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位置。

例:测试按钮和画布的鼠标事件,包括单击、按下、进入和退出等

 package test;
import java.awt.*;
import java.awt.event.*;
import java.applet.*; public class UseMouseEvent extends Applet implements MouseListener,MouseMotionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
Button btn;
public void init()
{
btn = new Button("演示鼠标事件");
add(btn);
//给按钮添加鼠标事件和鼠标移动事件
btn.addMouseListener(this);
btn.addMouseMotionListener(this);
//给画布添加鼠标事件和鼠标移动事件
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
//单击事件
public void mouseClicked(MouseEvent e)
{
Point p = new Point();
if(e.getSource() == btn)//getSource()发生事件的对象,从EventObject继承来的方法。 {
if(e.getClickCount() == 1)
{
btn.setLabel("单击鼠标");
}
else if(e.getClickCount() == 2)
{
btn.setLabel("双击鼠标");
}
}
else
{
if(e.getClickCount() == 1)
{
p = e.getPoint();
showStatus(p.x + "," + p.y + "单击鼠标");
}
else if(e.getClickCount() == 2)
{
p = e.getPoint();
showStatus(p.x + "," + p.y + "双击鼠标");
}
}
}
//进入事件
public void mouseEntered(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("进入Button");
else
showStatus("进入Applet");
}
public void mouseExited(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("退出Button");
else
showStatus("退出Applet");
}
//按下事件
public void mousePressed(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("按下鼠标");
else
showStatus("按下鼠标");
}
//释放事件
public void mouseReleased(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("松开鼠标");
else
showStatus("松开鼠标");
}
//移动事件
public void mouseMoved(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("移动鼠标");
else
showStatus("移动鼠标,新位置" + e.getX() + "," + e.getY());
}
//拖动事件
public void mouseDragged(MouseEvent e)
{
if(e.getSource() == btn)
btn.setLabel("拖动鼠标");
else
showStatus("拖动鼠标");
}
}

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)                            键入键时引发事件处理。

例:

 package test;

 import java.applet.Applet;
import java.awt.*;
import java.awt.RenderingHints.Key;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.TextEvent;
import java.awt.event.TextListener; public class UseKeyEvent extends Applet implements KeyListener,ActionListener, TextListener {
/**
*
*/
private static final long serialVersionUID = 1L;
Key key;
Button btn;
TextField txt;
public void init()
{
btn = new Button("演示键盘事件");
add(btn); btn.addKeyListener(this); this.addKeyListener(this);
txt = new TextField(25);
//添加事件监听者
txt.addActionListener(this);
txt.addTextListener(this);
//设置界面 add(txt,BorderLayout.NORTH); }
@Override
public void keyTyped(KeyEvent e) {//键入键时引发事件处理
// TODO 自动生成的方法存根
char ch = e.getKeyChar();
if(ch =='Y' || ch == 'y')
txt.setText ("同意");
else if (ch == 'N' || ch == 'n')
txt.setText ("反对");
else
txt.setText ("无效"); }
@Override
public void keyPressed(KeyEvent e) {
// TODO 自动生成的方法存根
btn.setLabel("按键时引发事件处理");
}
@Override
public void keyReleased(KeyEvent e) {
// TODO 自动生成的方法存根
btn.setLabel("释放键时引发事件处理");
}
@Override
public void textValueChanged(TextEvent e) {
// TODO 自动生成的方法存根 }
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根 }
}

二  多 线 程 机 制

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()//运行线程

{

...

}

}

例:多线程实例,主函数给予调用

 public class MThread
{
public static void main(String[] args)
{
System.out.println("Hello World!");
thread2 t1 = new thread2("线程实例1"); //创建线程实例
t1.start(); //调用
thread2 t2 = new thread2("线程实例2");
t2.start();
thread2 t3 = new thread2("线程实例3");
t3.start();
}
}
//自定义线程类thread2
class thread2 extends Thread
{
Thread thread; //定义线程实例
String str;
//构造函数
public thread2(String str)
{
this.str = str;
}
//启动线程
public void start()
{
thread = new Thread(this);
thread.start();
}
public void run()
{
int i = 0;
while(thread != null)
{
try
{
//计数到5时睡眠10秒
if(i == 5)
sleep(10000);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
System.out.println(str);
i++;
}
}
};

2.2.2. 实现接口Runnable

public interface Runnable

Runnable接口可以由任意试图实现线程机制的类来实现。接口包含一个run方法。

public void run()

对象实现Runnable接口时,创建一个线程,启动线程导致对象run方法的调用。

实现接口Runnable进行多线程设计的方法较为常用。下面给出一个例子。

例:编写Applet,实现Runnable接口进行简单的动画演示:三幅图片依次上移

 package test;

 import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit; import javax.print.DocFlavor.URL; public class UseRunnable extends Applet implements Runnable{
/**
*
*/
private static final long serialVersionUID = 1L;
Thread t;
Image imgs[];
int high,h1,h2,h3;
public void init()
{
high = getHeight()/3;
h1 = high;
h2 = high * 2;
h3 = high * 3;
imgs = new Image[3];
for (int i = 0; i < 3; i ++)
{
/*java.net.URL url=getDocumentBase();
imgs[i] = getImage(getDocumentBase(),(i+1) + ".jpg");*/
imgs[i]=Toolkit.getDefaultToolkit().getImage("E:\\WorkSpace\\ecplise-luna\\JavaTest\\src\\test\\"+(i+1)+".jpg ");
}
}
public void start()
{
t = new Thread(this);
t.start();
}
public void stop()
{
t = null;
}
//实现接口的run方法,获得动画效果
public void run()
{
while( t != null)
{
try
{
Thread.sleep(100);
repaint();
h1 --;
//上移,到顶点时睡眠
if(h1 == 0)
{
Thread.sleep(3000);
h2 = high;
}
//上移,到顶点时睡眠
h2 --;
if(h2 == 0)
{
Thread.sleep(3000);
h3 = high;
}
//上移,到顶点时睡眠
h3--;
if(h3 == 0)
{
Thread.sleep(3000);
h1 = high;
}
}catch(InterruptedException e){
System.out.println(e.getMessage());
}
}
}
public void paint(Graphics g)
{
//三幅图片依次显示
g.drawImage(imgs[0],0,h1,this);
g.drawImage(imgs[1],0,h2,this);
g.drawImage(imgs[2],0,h3,this);
}
public void update(Graphics g)
{
paint(g);
}
}