Web技术 第8章 Applet应用 龚涛 东华大学信息科学与技术学院 2016年3月 教材:《JSP程序员成长攻略》 网上课程:http://www.ytxxchina.com/taogong
知识框架 Applet是用Java语言编写的、运行于客户端浏览器上的小应用程序(Servlet运行于服务器端)。这些程序是直接嵌入到HTML页面(称为Java-powered页)中,当运行时Applet被下载到本地机器上执行。所以,在运行时它不受网络带宽的限制,可以提高Web页面的交互能力和动态执行能力,产生良好的多媒体效果。至今,Applet仍然是Java程序设计最具魅力的应用之一。 本章将介绍Applet编程方法和技巧,涉及的内容包括: Applet的基本结构 Applet类 Graphics类 多线程技术 两个应用实例
8.1 Applet概述 什么是Applet 由上一章介绍知道,Servlet是运用于服务器端的Java应用程序。与之对应,Applet则是运行在客户端的Java小程序。即Applet也是由Java语言编写的,但它是嵌套在客户端浏览器中的Java程序。当然,其运行的前提是浏览器必须支持Applet的嵌入。这时浏览器被称为Java兼容的浏览器,而包含Applet的网页则被称为Java-powered页,也称为Java支持的网页。目前,市场上占主流地位的IE浏览器是Java兼容的浏览器,这使得Applet的应用开发变得非常方便。 网页文件是利用<applet>标记将Applet包含到网页中的。当包含Applet的网页被执行而遇到<applet>标记时,就将编译后的Applet类下载到本地机器上并在本地机器上执行该Applet。因此,在执行时Applet不受网络带宽或者Modem存取速度的限制。 Applet可以绘制图形、控制字体和颜色、插入动画和声音等,使嵌入Applet的网页能够产生特殊的效果。此外,Applet还提供了一种窗口环境开发工具——抽象窗口工具箱AWT(Abstract Window Toolkit),这使得Applet通过利用用户计算机的GUI元素,建立标准的图形用户界面,如窗口、按钮、滚动条等。关于AWT,在本章中将有详细的介绍并配以相应的实例说明。
Applet的基本结构 下面的Applet(testApplet.java)是用于打印一个字符串——“这是我的第一个Applet应用程序!”。 /*** testApplet.java文件(D:\tomcat4\webapps\ROOT\ch8) ***/ import java.applet.*; import java.awt.*; public class testApplet extends Applet { public void paint(Graphics g) g.drawString("这是我的第一个Applet应用程序!",30,30); }
Applet的特点 显然,它具有Java类的一般结构。而作为Applet,其特点体现在: 必须继承java.applet.Applet类; 必须使用public来声明Applet; 其程序入口处不是main方法。 严格地说,Applet的基本结构如下: public class testApplet extends java.applet.Applet { // 编写代码的地方 }
testApplet.htm代码 为使Applet能够完成相应的功能,必须在上述结构中添加相应的代码,覆盖java.applet.Applet类的一些方法。例如,在testApplet.java文件中覆盖了java.applet.Applet类的paint()方法,以打印相应的字符串。 为观察上述Applet的运行效果,编写包含此Applet的HTML文件——testApplet.htm,其代码如下: <!-- 这是testApplet.htm文件(位于D:\tomcat4\webapps\ROOT\ch8) --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="testApplet.class" width="500" height="300"> </applet> </body> </html>
运行包含Applet的网页的方法 运行包含Applet的网页有两种方法: 一种是直接打开该网页文件(可以不启动Web服务器); 另一种是利用appletviewer命令来运行,这非常方便Applet的调试。 这两种方法的效果分别如图8.1和图8.2所示。
在网页中Applet运行的效果
使用appletviewer命令运行Applet的效果
8.2 熟悉Applet类 java.applet.Applet类的构造方法只有一种形式——public Applet()。此构造方法是由浏览器调用,以完成对Applet类实例的创建。下面介绍Applet类其他常用的方法。 影响Applet生命周期的五种方法: (1)public void init() 该方法是由浏览器或appletviewer调用,它是Applet中首先被调用的方法,且仅仅被调用一次。其作用是为Applet做一些初始化工作,如接收来自网页的参数值等。如果没有初始化需要,可以不覆盖此方法。 (2)public void start() 该方法仅接于init()方法之后由浏览器或appletviewer调用。但它不是仅仅被调用一次,而是每次重新访问Applet (转到其他页面,然后又重新返回包含Applet页面)时都被调用。因此,希望每次访问Applet时都要执行的代码应该放在此方法中。此方法也是根据需要进行覆盖。
影响Applet生命周期的五种方法 (3)public void stop() 该方法是在每次离开Applet所在页面或者Applet将要被销毁时由浏览器或appletviewer调用,这意味stop()也是可以多次执行。该方法也是根据需要而对它进行覆盖。它往往与start()方法搭配使用。例如,当我们离开Applet所在的页面时,显然希望停止Applet播放的动画,以释放系统资源,这时可以在stop()方法中写代码来完成;当我们重新进入该页面时,又希望这些动画重新被显示,于是可以在start()方法中写相应的代码来实现。 (4)public void destroy() 该方法是在关闭浏览器时被调用,用于通知此Applet它正在被销毁,以释放相应的系统资源。此方法也是根据需要而对它进行覆盖。destroy()与init()方法向对应,但作用相反。例如,使用线程的Applet是调用init()方法来创建线程,调用destroy()方法销毁它们。 显然,该方法只能被执行一次,执行完了,此Applet也就不复存在了。 (5)public void paint(Graphics g) 此方法是Applet中一个非常重要的方法,其作用是完成画面的显示工作。它的第一次执行在start()方法之后。此后,当浏览器窗口放大、缩小或是被别的窗口遮盖又重新出现时都会被调用。 paint()方法中带有一个Graphics类型的参数g,在其被调用时由浏览器或appletviewer自动创建一个Graphics对象并传递到该方法中。 pause
例8.1 查看关于Applet的生命周期示例的五种方法。 下面的Applet可以显示上述五种方法在Applet生命周期中的调用情况。 /** lifecycleApplet.java **/ import java.awt.*; import java.applet.*; public class lifecycleApplet extends Applet { TextArea wordpad=new TextArea(); int initNumber,startNumber,stopNumber,destroyNumber, paintNumber; String str; public lifecycleApplet() this.setLayout(new BorderLayout()); this.add(wordpad); str=""; } public void init() initNumber++; str=str+"init()方法第 "+initNumber+" 次被执行。\n"; wordpad.setText(str);
lifecycleApplet.java代码 public void start() { startNumber++; str=str+"start()方法第 "+startNumber+" 次被执行。\n"; wordpad.setText(str); } public void stop() stopNumber++; str=str+"stop()方法第 "+stopNumber+" 次被执行。\n"; public void destroy() destroyNumber++; str=str+"destroy()方法第 "+destroyNumber+" 次被执行。\n"; public void paint(Graphics g ) paintNumber++; str=str+"paint()方法第 "+paintNumber+" 次被执行。\n";
包含该Applet的网页文件 <!-- 这是lifecycleApplet.htm文件 --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="lifecycleApplet.class" width="500" height="300"> </applet> </body> </html>
lifecycleApplet.htm首次运行的结果 执行下列命令后,得到如图8.3所示的界面: D:\tomcat6\webapps\ROOT\ch8>appletviewer lifecycleApplet.htm
与图像显示相关的方法 (1)public URL getDocumentBase() 返回包含此Applet的网页文件的URL。例如,getDocumentBase().toString()将返回“http://localhost:8080/ch8/testApplet.htm”(相应的Applet类是放在D:\tomcat4\webapps\ROOT\ch8目录下) (2)public URL getCodeBase() 返回包含此Applet的网页文件的基URL。例如,getCodeBase().toString()将返回“http://localhost:8080/ch8/”。 (3)public Image getImage(URL url) 获取能在屏幕上显示的Image 对象,url为图像文件的绝对 URL。 (4)public Image getImage(URL url, String name) 该方法也是用于获取能在屏幕上显示的Image 对象,但它用到两个参数,其中第一个参数url表示图像文件的基URL,name表示相对于url参数的图像文件位置。
addPictoApplet.java文件 import java.applet.*; import java.awt.*; import java.net.*; public class addPictoApplet extends Applet { Image myImage; public void paint(Graphics g) g.drawImage(myImage,0,0,this); } public void init() myImage=getImage(getCodeBase(),"DSC01353.JPG");
addPictoApplet.htm文件 相应的HTML文件代码如下: <!-- 这是addPictoApplet.htm文件 --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="addPictoApplet.class" width="800" height="600"> </applet> </body> </html>
图像显示的方法 运行addPictoApplet.htm文件后,即可显示出相应的图片。 当然,我们亦可利用getImage()方法的另一种形式来完成图像的显示功能。相应的Applet代码如下: /*** addPictoApplet.java文件 ***/ import java.applet.*; import java.awt.*; import java.net.*; public class addPictoApplet extends Applet { Image myImage; URL url; public void paint(Graphics g) g.drawImage(myImage,0,0,this); } public void init() try url=new URL(getCodeBase().toString()+"DSC01353.JPG"); myImage=getImage(url); }catch(MalformedURLException e){} //必须捕获MalformedURLException异常
与音频文件播放相关的方法 (1)public AudioClip getAudioClip(URL url) 该方法用于加载由url指定绝对URL的音频文件。如果成功加载则返回一个AudioClip对象,否则返回null。通过AudioClip类提供的方法,我们可以实现对音频播放的控制。例如,利用AudioClip类提供的loop()方法和play()方法,就可以分别实现对音频文件的循环播放和一次性播放。 注意,Java目前只支持Sum公司的.au格式的音频文件,不支持如.wav等其他格式的音频文件。.au格式文件的优点是,压缩比比较高,有利于网上传播。如果需要的话,可利用相关软件将其他格式的音频文件转换为.au格式的音频文件。
利用getAudioClip()方法播放音频文件 /** audioApplet.java **/ import java.applet.*; import java.awt.*; public class audioApplet extends Applet { AudioClip audioClip; public void init() audioClip=getAudioClip(getCodeBase(),"audiofile.au"); //加载 } public void paint(Graphics g) if(audioClip!=null) g.drawString("音频文件已经成功加载,并以循环方式播放! ",10,15); else g.drawString("音频文件没有成功加载! ",10,15); public void start() if(audioClip!=null) audioClip.loop(); //循环播放 public void stop() if(audioClip!=null) audioClip.stop(); //暂停播放
相应的HTML文件 <!-- 这是audioApplet.htm文件 --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="audioApplet.class" width="500" height="300"> </applet> </body> </html>
文件audioApplet.htm的运行界面 该Applet用于播放文件audiofile.au,连同audioApplet.java和audioApplet.htm文件一起,它们都放在同一个目录下,即D:\tomcat6\webapps\ROOT\ch8目录下。执行audioApplet.htm文件将产生如图8.4所示的页面,并伴随着音乐的播放。
与音频文件播放相关的方法 (2)public AudioClip getAudioClip(URL url, String name) 该方法也是用于加载音频文件,并在成功加载时返回一个AudioClip对象,否则返回null。与前面加载方法不同的是,它有两个参数。其中,url表示音频文件的基URL,name表示相对于url参数的音频剪辑位置。 (3)public void play(URL url)和public void play(URL url, String name) play()用于一次性播放指定的音频文件。如果没有找到指定的音频文件,则没有任何效果。其参数意义与getAudioClip()方法一样,此不赘言。
其他方法 (1)public boolean isActive() 判断一个Applet是否处于活动状态,是则返回true,否则返回false。 (2)public void resize(int width,int height)、public void resize(Dimension d) 用于调整此Applet显示区域的大小。 (5)public String getParameter(String name) 该方法用于获取HTML标记中指定参数的值。如果在HTML标记中没有指定相应的参数,则返回null。 (6)public void showStatus(String msg) 用于将msg指定的字符串显示在浏览器的状态栏中。 (7)public String getAppletInfo() 返回有关Applet的相关信息,通常是作者、版本和版权等信息。但需要覆盖此方法,否则它返回空值。
8.3 熟悉Graphics类 在Applet应用中,界面的绘制、着色以及文本输出等都是经常使用的操作。Java 语言中java.awt.Graphics类提供了这些功能。在需要重新绘制组件时,浏览器将创建Graphics类的一个实例,然后调用以该实例作为参数的paint()方法。利用Graphics类提供的方法,可以在Applet界面上绘制线条、椭圆、矩形、输出文本信息等,而且可以控制线条的大小、颜色等。 在介绍Graphics类的方法之前,我们先了解一下图形上下文的坐标系(以下简称坐标系)与屏幕二维坐标系之间的关系。
Graphics类的主要方法 (1)void drawLine(int x1, int y1, int x2, int y2) 该方法用于在点 (x1, y1) 和点 (x2, y2) 之间画一条线段。例如,下面的代码用于从点(20,20)到点(300,100)之间绘制一条线段: public void paint(Graphics g) { g.drawLine(20,20,300,100); } (2)void drawRect(int x, int y, int width, int height) 绘制矩形的边框,矩形左上角对应的点的坐标为(x, y),矩形的宽(水平方向)和高(垂直方向)分别width和height,单位为像素。注意,width和 height不是分别为矩形右下角的坐标(右下角的坐标为(x+width, y+height))。
Graphics类的主要方法 (3)void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) 该方法用于绘制圆角矩形。其中,参数x,y,width和height与drawRect()方法中的参数的意义是一样的;arcWidth和arcHeight分别指定圆角的弧宽和弧高。 例如,以下代码绘制的绘制圆角矩形如图8.6所示。 public void paint(Graphics g) { g.setColor(Color.RED); //设置当前颜色 g.drawRoundRect(50,50, 300,200, 50,50); //绘制圆角矩形 }
使用drawRoundRect ()方法绘制圆角矩形
Graphics类的主要方法 (4)void drawOval(int x, int y, int width, int height) 该方法用于绘制以点(x, y)为外切矩形的左上角对应的点、 外切矩形的宽和高分别为width和geight的椭圆(边框)。如果width和geight相等,则相应的椭圆就变为圆。 例如,下面代码绘制出来的图形效果如图8.7所示。 public void paint(Graphics g) { g.drawOval(100,100, 300,200); }
使用drawOval()方法绘制椭圆
Graphics类的主要方法 (5)void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) 用于绘制闭合多边形,其中数组xPoints和yPoints中分别存放多边形节点的x坐标和y坐标(顺时针方向),nPoints表示节点数量。 例如,下列代码绘制的图形如图8.8所示。 public void paint(Graphics g) { g.setColor(Color.RED); int[] xPoints={10,200,50,10}; int[] yPoints={10,200,300,250}; int nPoints=4; g.drawPolygon(xPoints, yPoints,nPoints); //绘制四个点的闭合多边形 }
使用drawPolygon()方法绘制闭合多边形
Graphics类的主要方法 (6)drawPolyline(int[] xPoints, int[] yPoints, int nPoints) 用于绘制一系列连接线,连接线的节点坐标由数组xPoints和yPoints指定,nPoints指定节点数量。如果第一个节点和最后一个节点不同,则图形不是闭合的。 (7)void drawString(String str, int x, int y) 该方法在前面已经多次接触,它用于从点(x,y)开始用当前字体显示由str给定的文本。 例如,下列代码首先将当前字体设置为28号的隶书字体,然后按照这个字体在指定位置显示“中华人民共和国万岁!”: public void paint(Graphics g) { g.setFont(new Font("隶书",0,28)); g.drawString("中华人民共和国万岁!",10,100); }
Graphics类的主要方法 (8)void drawString(AttributedCharacterIterator iterator, int x, int y) 该方法的作用是从点(x,y)开始输出由指定迭代器给定的文本。 (9)boolean drawImage(Image img, int x, int y, ImageObserver observer) 将由img指定的图像对象从点(x,y)处开始显示,显示的尺寸与原始尺寸一样。observer 为当转换了更多图像时要通知的对象。 此方法在任何情况下都立刻返回:如果图像已经完全装入,并且其像素不再发生改变(抖动),则返回 true,否则返回false。 例如下面代码将完成装入图像20.jpg,并从(0,0)处开始显示。 myImage=getImage(getCodeBase(),"20.jpg"); g.drawImage(myImage,0,0,this);
Graphics类的主要方法 (10)boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) 该方法用于将图像缩放到适合指定矩形内部显示,图像的宽和高分别由width和height指定,bgcolor用于指定在图像非透明部分下绘制的背景色。 例如,下面代码表示将指定的图像缩放到100*200的区域内,而不管它的原始尺寸为多少: g.drawImage(myImage,0,0,200,100,Color.BLUE,this); (11)void fillRect(int x, int y, int width, int height) 用当前颜色填充从点(x,y)开始、宽为width、高为height的矩形区域。例如,下面代码执行将得到如图8.9所示的界面。 public void paint(Graphics g) { g.setColor(Color.BLUE); //设置前景 g.fillRect(100,100,80,60); //填充矩形区域 }
使用fillRect()方法绘制矩形区域
Graphics类的主要方法 (12)void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) 用当前颜色填充指定的圆角矩形。 (13)void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) 填充覆盖指定矩形的圆弧或椭圆弧。 (14)void fillOval(int x, int y, int width, int height) 使用当前颜色填充指定的椭圆区域。 (15)void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) 使用当前颜色填充指定的多边形区域。
Graphics类的主要方法 (16)void clearRect(int x, int y, int width, int height) 使用当前绘图表面的背景色填充指定矩形区域,以到达清除矩形区域的目的。 例如,下面代码的功能是,先绘制一个蓝色的矩形区域,然后用背景色清楚其中间的一个小矩形区域,结果如图8.10所示: public void paint(Graphics g) { setBackground(Color.GRAY); //设置背景色 g.setColor(Color.BLUE); //设置前景色 g.fillRect(10,10,300,200); //用前景色绘制矩形区域 g.clearRect(100,100,100,50); //用背景色清除指定的矩形区域 }
使用clearRect()方法清除矩形区域
Graphics类的主要方法 (17)void draw3DRect(int x, int y, int width, int height, boolean raised) 突出绘制的矩形的边框,以达到3D效果,raised为true表示突出,否则不突出。 (18)void fill3DRect(int x, int y, int width, int height, boolean raised) 突出绘制的矩形区域,以达到3D效果,raised为true表示突出,否则不突出。 (19)Graphics create() 该方法用于创建一个新的Graphics对象,它是此Graphics对象的副本。 (20)Graphics create(int x, int y, int width, int height) 该方法使用新的转换和剪贴区域创建一个新的Graphics对象。 (21)void dispose() 该方法用于释放此图形的上下文并释放它所占用的系统资源。 (22)Color getColor() 获取在图形上下文中当前绘图颜色的对象。
(23)void setColor(Color c) 将当前的绘图颜色设置为Color对象c所定义的颜色。例如,下面语句是将前景色设置为品红色: g.setColor(Color.MAGENTA); 其中,MAGENTA表示品红色的颜色常量,也可小写为magenta。我们也可以使用颜色的RGB值来进行颜色设置。这样,与上面语句等价的语句还有: g.setColor(Color.magenta); g.setColor(new Color(255, 0, 255)); //使用品红色的RGB值
定义一个能够设置线条的粗细、用于绘制矩形的函数 例8.4 定义一个能够设置线条的粗细、用于绘制矩形的函数。 基本思路是,先用前景色填充一个矩形区域,然后在其中用背景色填充一个“小一点”矩形区域,使得剩下的“边框”的大小刚好为pen。该函数代码如下: public void penDrawRect(Graphics g, int x, int y, int width, int height, int pen) { g.fillRect(x,y,width,height); //绘制矩形区域 Color bc=getBackground(); //获取背景塞 Color c=g.getColor(); //获取前景色 g.setColor(bc); //将背景色设置为前景色 //用背景色填充一个区域,使得剩下的周围边框的粗度为pen g.fillRect(x+pen,y+pen,width-2*pen,height-2*pen); g.setColor(c); //恢复前景色 }
绘制结果 例如,如果预绘制一个线条大小为10的矩形,可以调用下列语句来完成,结果如图8.11所示。 penDrawRect(g, 50,50,300,200, 20);
解决图像的抖动问题 如果在Applet中直接加载图像,特别是动态图像,将产生严重的抖动。 为了防止屏幕重画出现的抖动,先创建一个离屏的绘图区(缓存区),在此重画图像画面,然后将画完的画面一次性显示到屏幕上。这样可以有效减少图像的抖动程度。GraphicTingle.java文件可更改如下: /** GraphicTingle.java文件 **/ import java.applet.*; import java.awt.*; import java.net.*; public class GraphicTingle extends Applet { Image myImage; URL url; Image offScreenImage; Graphics offScreenGraphics;
GraphicTingle.java文件 public void paint(Graphics g) { offScreenGraphics.drawImage(myImage,60,60,this); offScreenGraphics.setFont(new Font("隶书",0,28)); offScreenGraphics.setColor(Color.red); offScreenGraphics.drawString("这是动态图像",100,30); g.drawImage(offScreenImage,0,0,this); //一次性显示画面 } public void init() offScreenImage = createImage(500,500); //创建离屏的绘图区 offScreenGraphics = offScreenImage.getGraphics(); //取得绘图环境 try url=new URL(getCodeBase().toString()+"411_3.gif"); myImage=getImage(url); }catch(MalformedURLException e){}
在Applet中显示动态图像 该Applet中图像抖动性大为降低,图8.12是截获的一个动画画面。
8.4 使用鼠标事件编程 正确运用鼠标事件可以有效提高Applet程序的灵活性、简化Applet界面的复杂性。 在AWT中,常用的鼠标事件主要包括: (1)MOUSE_DOWN事件 当鼠标左键按下时会产生该事件。该事件发生时,系统会调用mouseDown()方法来处理该事件。mouseDown()方法的声明格式如下: public boolean mouseDown(Event e,int x,int y) { …… } 该方法包含三个参数,其中e就是事件本身,x,y则是该事件发生时鼠标所在位置的坐标值。因此,通过x和y,我们可以判断鼠标是在哪个位置被按下。
常用的鼠标事件 (2)MOUSE_UP事件 该事件是在鼠标被按下后再放开时发生的,对应的事件处理方法如下: public boolean mouseUp(Event e,int x,int y) { …… } 其参数意义同mouseDown()方法。 (3)MOUSE_MOVE事件 当鼠标移动的时产生该事件。其事件处理方法如下: public boolean mouseMove(Event e,int x,int y) 其中,x和y表示鼠标移动前一刻其所在位置的坐标。
常用的鼠标事件 (4)MOUSE_DRAG事件 当被拖动时该事件被触发。其事件处理方法如下: 当被拖动时该事件被触发。其事件处理方法如下: public boolean mouseDrag(Event e,int x,int y) { …… } (5)MOUSE_ENTER事件 当鼠标进入到Applet显示区域时,该事件被触发。其事件处理方法如下: public boolean mouseEnter(Event e,int x,int y) } (6)MOUSE_EXIT事件 当鼠标从Applet显示区域移出时,该事件被触发。其事件处理方法如下: public boolean mouseExit(Event e,int x,int y)
关于运用鼠标事件的例子 例8.5 利用鼠标事件和Graphics类的绘图功能显示一个“按钮”,当点击该按钮时可以读取服务器上指定的文件并显示出来。 “按钮”的显示可以通过绘制矩形区域来实现,设置“按钮”的颜色为灰色: offScreenGraphics.setColor(Color.gray); offScreenGraphics.fill3DRect(posx,posy,width,height,b); 其中,b为boolean变量,用于控制“按钮”是处于凸还是凹的状态,其赋值分别鼠标事件的mouseDown()和mouseUp()方法中完成。这样,在点击“按钮”时就可以具有很强的三维动态感。 另外,出于安全因素考虑,Applet并不允许操作客户端的文件(包括读写操作)。需要注意的是,如果在Applet运用了对客户端文件的读写操作,在编译时并不出现错误,但在浏览器中运行时将产生错误。然而,通过URL对象和URLConnection对象,我们可以读取服务器上指定的一些文件。本例中,我们将读取服务器上的http://localhost:8080/ch8/test.txt文件,其内容仅包含一行文字——“你已成功下载服务器端的数据”。
Applet文件 为此,构造如下的Applet文件: /**mouseEvent.java文件**/ /**mouseEvent.java文件**/ import java.awt.event.*; import java.awt.*; import java.applet.*; import java.net.*; import java.io.*; public class mouseEvent extends Applet { int posx,posy,width,height; int n=0; String txtStr=""; boolean b; Image offScreenImage; Graphics offScreenGraphics; public void init() super.init(); resize(400,300); Dimension d=this.size(); offScreenImage=createImage(d.width,d.height); offScreenGraphics = offScreenImage.getGraphics(); posx=150; //设置按钮显示的位置 posy=250; width=80; //设置按钮的宽度和高度 height=40; b=true; //按钮凹凸的控制变量 }
Applet文件 public void paint(Graphics g) { offScreenGraphics.setFont(new Font("隶书",0,28)); offScreenGraphics.drawString(txtStr,30,100); //显示服务器端的数据 offScreenGraphics.setColor(Color.gray); //开始绘制按钮 offScreenGraphics.fill3DRect(posx,posy,width,height,b); offScreenGraphics.setFont(new Font("宋体",0,13)); offScreenGraphics.setColor(Color.RED); offScreenGraphics.drawString("下载按钮",posx+15,posy+25); offScreenGraphics.setColor(Color.black); g.drawImage(offScreenImage,0,0,this); } public boolean mouseDown(Event e,int x,int y) //实现按钮功能 if((x>=posx)&&(x<=posx+width-1)&&(y>=posy)&&(y<=posy+height-1)) b=false; repaint(); return true; public boolean mouseUp(Event e,int x,int y) //实现按钮功能 b=true; String fu="http://localhost:8080/ch8/test.txt"; txtStr=getServerFile(fu);
Applet文件 public String getServerFile(String fileurl) //读取服务器端数据的函数 { String txt=""; URL url; URLConnection c; try url=new URL(fileurl); c=url.openConnection(); c.connect(); InputStream is; int ch; is=c.getInputStream(); ch=is.read(); while(ch!=-1) txt=txt+(char)ch; } txt=new String(txt.getBytes("8859_1")); }catch(IOException ioe) {txt=ioe.toString();} }catch(MalformedURLException e) {txt=e.toString();} return txt; }//end class
相应的HTML文件 然后,构造相应的HTML文件: <!-- 这是mouseEvent.htm文件 --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="mouseEvent.class" width="550" height="300"> </applet> </body> </html> 将编译mouseEvent.java产生的mouseEvent.class类,连同mouseEvent.htm文件都保存在D:\tomcat6\webapps\ROOT\ch8目录下。接着利用appletviewer运行mouseEvent.htm文件,结果如图8.13所示。这表明该Applet已经成功读取服务器端的文件。
利用鼠标事件处理程序实现按钮功能
8.5 多线程技术 线程的概念 在运行的时候,程序中允许存在多个并发执行的指令流,每个指令流都称为一个线程。线程间彼此相对独立,同时也允许不同线程之间进行数据交换等。显然,多线程是相对单线程而言的,它是指多个并发执行的指令流。 多线程为提高程序执行效率提供了一种机制。例如,在网络编程中,有时候网络传输速度比较慢,如果这时让整个程序都在等待网络的传输结果,那么其效率显然不理想;如果能够将程序分成几个线程,让其中一个线程去执行等待网络传输结果,其他线程去完成与传输结果无关的任务。这样,显然可以提高程序的执行效率。 还有一个与线程十分相似的概念是进程。实际上,线程和进程是两个完全不同的概念。简单而言,进程是由线程构成;进程是一个程序的执行序列,而线程是进程中的一个子序列;进程是由操作系统负责管理,线程则由程序负责管理,在程序中可以创建线程、启动线程、挂起线程、终止线程等。线程也称为轻量级进程。 线程和进程的最大区别体现在,线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间。这个特点也使得线程间的通讯较进程间的通讯变得更为简单。
线程的生命周期 线程一般都体现出很强的生命周期特性。从产生到灭亡,线程的生命周期主要分为以下5种状态: 新建状态:线程的对象已经被初始化完毕,但未被调度,更没有运行run方法。 可执行状态(runnable):线程已经被调度,正放在等待队列中,等待CPU资源,但还没有运行run方法。 运行状态(running):线程正在执行run方法,拥有对CPU的控制权。 挂起状态(blocked):在这种状态下,线程不能被调度,也就不能进入等待队列,直到有相应的事件唤醒它为止。 停止状态(dead):线程生命已经结束,不能再运行了。这可能是线程正常运行结束后的结果,也可能是被其他线程注销了。
创建线程 在Java中,创建线程有两种方法: 一种是通过继承Thread类的方法来创建一个线程; 另一种是通过实现Runnable接口来创建线程。 1 通过继承Thread类创建线程 Java语言提供了java.lang.Thread类来支持多线程编程,利用该类提供的大量方法我们可以方便地控制着各个线程。 例8.6 显示线程的工作原理 本例是一个Java应用程序的例子,它通过继承Thread类来创建三个线程,在线程中覆盖了run()方法。在run()方法中,让不同的线程睡眠不同的时间,以观察线程的执行过程。该程序对应ThreadExample.java文件,其代码如下: /**ThreadExample.java文件**/ import java.io.*; public class ThreadExample extends java.lang.Thread { String ThreadName="";
ThreadExample.java文件 public ThreadExample(String name) { ThreadName=name; System.out.println(ThreadName+"已被创建完毕!"); } public void run() while(true) //线程的运行一般是采用死循环 System.out.println("这是"+ThreadName+"在运行..."); int tm=0; if(ThreadName.equals("线程1")) tm=500; //线程1睡眠500毫秒 else if(ThreadName.equals("线程2")) tm=1000; //线程2睡眠1000毫秒 else if(ThreadName.equals("线程3")) tm=2000; //线程3睡眠2000毫秒 try{Thread.sleep(tm);} catch(InterruptedException e){} public static void main(String args[]) //创建并执行三个线程 new ThreadExample("线程1").start(); new ThreadExample("线程2").start(); new ThreadExample("线程3").start(); }//end class
运行结果 在控制台上编译ThreadExample.java后,运行ThreadExample.class类,结果如图8.14所示。
通过实现Runnable接口创建线程 2 通过实现Runnable接口创建线程 Runnable接口与Thread类一样,都是在java.lang包中定义的。该接口只提供了一个无参数的抽象方法——run()方法,声明如下: publilc interface java.lang.Runnable { public abstract void run(); } 在Applet中,要实现Runnable接口中的run()方法,即在这里编写线程的处理代码。然后通过将当前定义的类(Applet类的子类)的实例作为参数传递给Thread类的构造方法,以作为初始化参数创建相应数量的Thread实例。 注意,Runnable接口本身并不支持线程,它只声明了一个run()方法。要创建线程还必须由Thread类来实现,通过Thread类的构造函数将Runnable接口的run()方法“转变为”线程自己的方法,从而利用run()方法中编写的代码来完成相应的任务。
利用Runnable接口来编写多线程程序 相同的效果。 /**RunnableExample.java文件**/ import java.awt.event.*; import java.awt.*; import java.applet.*; public class RunnableExample extends Applet implements Runnable //使用接口 { String ThreadName=""; Thread thread1=null,thread2=null,thread3=null; public void init() //以下创建三个线程,并分别为之命名 thread1=new Thread(this); thread1.start(); thread1.setName("线程1"); thread2=new Thread(this); thread2.start(); thread2.setName("线程2"); thread3=new Thread(this); thread3.start(); thread3.setName("线程3"); }
RunnableExample.java文件 public void run() { while(true) Thread tmThread = Thread.currentThread(); //取当前线程的名称 int tm=0; ThreadName=tmThread.getName(); if(ThreadName.equals("线程1")) tm=500; else if(ThreadName.equals("线程2")) tm=1000; else if(ThreadName.equals("线程3")) tm=2000; System.out.println("这是 "+ThreadName+" 在运行..."); //在控制台上输出线程的运行信息 try{Thread.sleep(tm);} catch(InterruptedException e){} } public void paint(Graphics g) g.setFont(new Font("隶书",0,28)); g.setColor(Color.red); g.drawString("三个线程正在运行...",30,100); //在Applet上输出相应的提示信息 }//end class
testApplet.htm文件 以下是相应的HTML文件: <!-- 这是testApplet.htm文件 --> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="RunnableExample.class" width="500" height="300"> </applet> </body> </html> 将编译RunnableExample.java文件后产生的RunnableExample.class文件,连同testApplet.htm文件一起保存在D:\tomcat6\webapps\ROOT\ch8目录下。然后运行testApplet.htm文件,结果如图8.15所示。从图中可以看出各线程的运行情况。
testApplet.htm文件的运行结果
8.6 使用Applet显示梵塔问题的求解过程 什么是梵塔问题 梵塔问题是印度的一个神话故事。说是有一座印度教的神庙,这庙有一块黄铜板,板上插着三根针。当印度教的主神梵天在创造世界的时候,在其中的一根针上从下到上放了半径由大到小的64片圆金片环,天神梵天要求这庙的僧侣,把这些金片全部由一根针移到另外一根指定的针上,且一次只能移一片环,不管在什么情况下,金片环的大小次序不能变更,即小金片环永远只能放在大金片环的上面。 这就是著名的梵塔问题(或称汉若塔问题,Towers of Hanoi)。这是一个NP难问题,经常作为规约问题的例子来讲解。这个问题可以转述如下: 有3个柱子(1,2和3)和n个不同尺寸的圆盘。在每个圆盘的中心有一个孔,所以圆盘可以堆叠在柱子上。最初,n个圆盘都堆在柱子1上:最大的圆盘在底部,最小的圆盘在顶部。要求把所有圆盘都移到柱子3上,每次只许移动一个,而且只能先搬动柱子顶部的圆盘,还不许把尺寸较大的圆盘堆放在尺寸较小的圆盘上。对n=3的情况,这个问题的初始配置和目标配置分别如图8.16和图8.17所示。
梵塔问题的初始配置
梵塔问题的目标配置
梵塔问题显示的设计思想 本节中,我们希望利用Applet来显示梵塔问题的解决过程,包括能够设置圆盘的个数,观察圆盘的移动过程(从哪跟柱子移到哪跟柱子);另外,还可以加快或降低圆盘移动的速度、随时停止或暂停移动过程、或恢复初始状态等。图8.18是将圆盘个数设置为5个的初始配置的Applet界面。
说明 对于如图8.18所示的界面,说明如下: “Number++”按钮:每点一次该按钮,圆盘个数就增加一个,最多为8个(理论上可以加到无数多个,但限于显示空间,最多只能加8个)。 “Number++”按钮:每点一次该按钮,圆盘个数就减少一个,直到为0为止。 “Play”按钮:点击该按钮,圆盘移动过程显示开始。 “Stop”按钮:点击该按钮,将暂停显示过程。 “Resume”按钮:点击该按钮,将恢复被暂停的显示过程。 “Speed++”按钮和“Speed”按钮:这两个按钮分别用于增加和减慢显示速度。 该显示问题的核心算法是利用递归思想来设计的。如果用hanoi(int n,int x,int y,int z)表示将柱子x上的n个圆盘移到柱子z上,y作为辅助柱子用;Move(x,z)表示将柱子x上的圆盘(这时要保证柱子x上只有一个圆盘)直接移到柱子z,同时显示移动过程。于是,显示问题的核心算法可描述如下:
核心算法 public void hanoi(int n,int x,int y,int z) { if(n==1) Move(x,z); //如果柱子x上只有一个圆盘,则可直接降之移到柱子z上 } else hanoi(n-1, x,z,y); //将柱子x上的n-1个圆盘先移到柱子y上,在此过程柱子z作为辅助柱子 Move(x,z); //将柱子x上唯一的一个圆盘移到柱子z hanoi(n-1,y,x,z); //最后将柱子y上的n-1个圆盘移动到柱子z上,柱子x作为辅助柱子 程序中设置了8个圆盘,这8个圆盘实际上就是8张图片。因此,移动圆盘实际上就是移动图片。移动图片的原理是,按钮指定文件名加载图片,然后通过交替使用删除和重画图片的操作来达到显示图片移动的效果。因为删除时图片所在的位置与重画时图片所在的位置相差很小,而且删除和重画的时间间隔很短,所以在视觉上就给人以移动的感觉。表8.2给出了图片与其文件名的对应关系。
图片与文件名的对应关系
梵塔Applet的实现 以下是梵塔Applet程序的实现代码。 /*** hanoiApplet.java文件 ***/ /*** hanoiApplet.java文件 ***/ import java.awt.event.*; import java.awt.*; import java.applet.*; public class hanoiApplet extends Applet implements Runnable { //初始化参数 int p1=3; int p2=0; int p3=0; int speed = 10,val=4,st=0; int[] a=new int[p1]; int[] colx1 = new int[9]; int[] coly1 = new int[9]; int[] colr1 = new int[9]; Image[] img1 = new Image[9]; int[] colx2 = new int[9]; int[] coly2 = new int[9]; int[] colr2 = new int[9]; Image[] img2 = new Image[9]; int[] colx3 = new int[9]; int[] coly3 = new int[9]; int[] colr3 = new int[9]; Image[] img3 = new Image[9]; Image offScreenImage; Graphics offScreenGrahpics;
梵塔Applet程序代码 boolean finish=false; //循环控制变量 boolean redo=true; Image imgBack = null; Image[] imgPai = new Image[9]; int[] r = new int[9]; int renew=0; Thread paintThread = null; int w=60; int h=25; int BuLevel=336; int LaLevel=352; int BuStart=10; int LaStart=22; int BuVal=75; int LaVal=75; int[] state = new int[8]; Color[] buttonColor = new Color[8];
梵塔Applet程序代码 public boolean s(int n) { if(n==0) return false; return true; } public boolean able(int n) if(buttonColor[n]==Color.pink) return true; return false; public void makeUnable(int n) buttonColor[n]=Color.gray; state[n]=0; repaint(); public void makeAble(int n) buttonColor[n]=Color.pink; state[n]=1; public boolean mouseDown(Event event,int x,int y) for(int i=0;i<=6;i++) if((x>=BuStart+i*BuVal)&&(x<=BuStart+i*BuVal+w)&&(y>=BuLevel)&&(y<=BuLevel+h)) state[i+1]=0;
梵塔Applet程序代码 if((i==0)&&(able(i+1))) //Colomn 1 { if(p1<8) { p1++; buttonColor[1]=Color.pink; makeAble(2); makeAble(3); } else {makeUnable(1);} else if((i==1)&&(able(i+1))) //Colomn 2 if(p1>0) { p1--; if(p1==0) buttonColor[2]=Color.gray; state[2]=0; repaint(); makeUnable(3); buttonColor[2]=Color.pink; makeAble(1);
梵塔Applet程序代码 else { makeUnable(2); makeUnable(3); } else if((i==2)&&(able(i+1)))//Colomn 3:play if(p1>0) st=1; makeUnable(1); makeAble(4); else makeUnable(i+1); else if((i==3)&&(able(i+1)))//Colomn 4:stop st=0; makeAble(3); makeUnable(4);
梵塔Applet程序代码 else if((i==4)&&(able(i+1)))//Colomn 5:speed+ { if(val<30) {val++;makeAble(6);} } else if((i==5)&&(able(i+1)))//Colomn 6:speed- if(val>1) {val--;makeAble(5);} else if((i==6)&&(able(i+1)))//Colomn 7:renew for(int j=1;j<=8;j++) img1[j]=imgPai[j]; colx1[1] = 82; coly1[1] = 215;colr1[1] = r[1]; colx1[2] = 82; coly1[2] = 205;colr1[2] = r[2]; colx1[3] = 82; coly1[3] = 195;colr1[3] = r[3]; colx1[4] = 82; coly1[4] = 185;colr1[4] = r[4]; colx1[5] = 82; coly1[5] = 175;colr1[5] = r[5]; colx1[6] = 82; coly1[6] = 165;colr1[6] = r[6]; colx1[7] = 82; coly1[7] = 155;colr1[7] = r[7]; colx1[8] = 82; coly1[8] = 145;colr1[8] = r[8]; p1=3;p2=0;p3=0; renew=1; st=0; makeAble(1); makeAble(2); makeAble(3); makeUnable(4); repaint(); return true;
梵塔Applet程序代码 public boolean mouseUp(Event event,int x,int y) { for(int i=0;i<=6;i++) if((x>=BuStart+i*BuVal)&&(x<=BuStart+i*BuVal+w)&&(y>=BuLevel)&&(y<=BuLevel+h)) state[i+1]=1; if(buttonColor[1]==Color.gray) state[1] = 0; if(buttonColor[2]==Color.gray) state[2] = 0; if(buttonColor[3]==Color.gray) state[3] = 0; if(buttonColor[4]==Color.gray) state[4] = 0; if(buttonColor[5]==Color.gray) state[5] = 0; if(buttonColor[6]==Color.gray) state[6] = 0; if(buttonColor[7]==Color.gray) state[7] = 0; if(i==1) if(p1==0) makeUnable(2); if(renew==1) state[7]= 0; if(i==4) if(val>=30) makeUnable(5); if(i==5) if(val<=1) makeUnable(6); } repaint(); return true;
梵塔Applet程序代码 public void init() { r[1]=70;r[2]=65;r[3]=60;r[4]=55;r[5]=50;r[6]=45;r[7]=40;r[8]=35; offScreenImage = createImage(770,379); offScreenGrahpics = offScreenImage.getGraphics(); for(int i=0;i<9;i++) colx1[i] = 0; coly1[i] = 0;colr1[i] = 0;img1[i]=null; colx2[i] = 0; coly2[i] = 0;colr2[i] = 0;img2[i]=null; colx3[i] = 0; coly3[i] = 0;colr3[i] = 0;img3[i]=null; } imgBack = getImage(getCodeBase(),"images/CircleBack.gif"); img1[1]=getImage(getCodeBase(),"images/Circle1.gif"); img1[2]=getImage(getCodeBase(),"images/Circle2.gif"); img1[3]=getImage(getCodeBase(),"images/Circle3.gif"); img1[4]=getImage(getCodeBase(),"images/Circle4.gif"); img1[5]=getImage(getCodeBase(),"images/Circle5.gif"); img1[6]=getImage(getCodeBase(),"images/Circle6.gif"); img1[7]=getImage(getCodeBase(),"images/Circle7.gif"); img1[8]=getImage(getCodeBase(),"images/Circle8.gif"); for(int jj=1;jj<=8;jj++) imgPai[jj]=img1[jj]; colx1[1] = 82; coly1[1] = 215;colr1[1] = r[1]; colx1[2] = 82; coly1[2] = 205;colr1[2] = r[2]; colx1[3] = 82; coly1[3] = 195;colr1[3] = r[3]; colx1[4] = 82; coly1[4] = 185;colr1[4] = r[4]; colx1[5] = 82; coly1[5] = 175;colr1[5] = r[5]; colx1[6] = 82; coly1[6] = 165;colr1[6] = r[6];
梵塔Applet程序代码 colx1[7] = 82; coly1[7] = 155;colr1[7] = r[7]; colx1[8] = 82; coly1[8] = 145;colr1[8] = r[8]; coly1[0] = 225;coly2[0]=225;coly3[0]=225; for(int i=0;i<8;i++) state[i] = 1; for(int i=0;i<8;i++) buttonColor[i]=Color.pink; makeUnable(4); makeUnable(7); } public void start() { if(paintThread==null) paintThread = new Thread(this); paintThread.start(); public void stop() if(paintThread!=null) paintThread.stop(); public void update(Graphics g) paint(g);
梵塔Applet程序代码 public void run() { for(;!finish;) makeUnable(7); for(;st==0;) try{Thread.sleep(100);} catch(InterruptedException e){} } renew=0; hanoi(p1, 1,2,3); makeAble(7); for(;renew==0;) if(redo) {finish=false;p1=3;p2=0;p3=0;} else finish=true; repaint(); }//end run
梵塔Applet程序代码 public void hanoi(int n,int x,int y,int z) { for(;st==0;) try{Thread.sleep(100);} catch(InterruptedException e){} } if(n==1) Move(x,z); else hanoi(n-1, x,z,y); hanoi(n-1,y,x,z); public void Move(int col1,int col2) int t = 0; switch(col1) case 1: for(;coly1[p1]>=120;coly1[p1]-=val) repaint(); try{Thread.sleep(speed);}
梵塔Applet程序代码 coly1[p1]=120;repaint(); if(col2==2) t=260;else if(col2==3) t=430; for(;colx1[p1]<=t;colx1[p1]+=val) { repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} } colx1[p1]=t;repaint(); switch (col2) case 2: for(;coly1[p1]<=coly2[p2]-10;coly1[p1]+=val) coly1[p1]=coly2[p2]-10;repaint(); p2++; colx2[p2]=colx1[p1];colx1[p1]=0; coly2[p2]=coly1[p1];coly1[p1]=0; colr2[p2]=colr1[p1];colr1[p1]=0; img2[p2] =img1[p1];img1[p1]=null; p1--; break; case 3:
梵塔Applet程序代码 for(;coly1[p1]<=coly3[p3]-10;coly1[p1]+=val) { repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} } coly1[p1]=coly3[p3]-10;repaint(); p3++; colx3[p3]=colx1[p1];colx1[p1]=0; coly3[p3]=coly1[p1];coly1[p1]=0; colr3[p3]=colr1[p1];colr1[p1]=0; img3[p3] =img1[p1];img1[p1]=null; p1--; break; case 2: for(;coly2[p2]>=120;coly2[p2]-=val)
梵塔Applet程序代码 coly2[p2]=120;repaint(); if(col2==1) { t=82; for(;colx2[p2]>=t;colx2[p2]-=val) repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} } else if(col2==3) t=430; for(;colx2[p2]<=t;colx2[p2]+=val) colx2[p2]=t;repaint(); switch(col2) case 1: for(;coly2[p2]<=coly1[p1]-10;coly2[p2]+=val)
梵塔Applet程序代码 coly2[p2]=coly1[p1]-10;repaint(); p1++; colx1[p1]=colx2[p2];colx2[p2]=0; coly1[p1]=coly2[p2];coly2[p2]=0; colr1[p1]=colr2[p2];colr2[p2]=0; img1[p1] =img2[p2];img2[p2]=null; p2--; repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} break; case 3: for(;coly2[p2]<=coly3[p3]-10;coly2[p2]+=val) { } coly2[p2]=coly3[p3]-10;repaint(); p3++; colx3[p3]=colx2[p2];colx2[p2]=0; coly3[p3]=coly2[p2];coly2[p2]=0; colr3[p3]=colr2[p2];colr2[p2]=0; img3[p3] =img2[p2];img2[p2]=null;
梵塔Applet程序代码 case 3: for(;coly3[p3]>=120;coly3[p3]-=val) { repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} } coly3[p3]=120;repaint(); if(col2==1) t=82;else if(col2==2) t=260; for(;colx3[p3]>=t;colx3[p3]-=val) colx3[p3]=t;repaint(); switch (col2) case 1: for(;coly3[p3]<=coly1[p1]-10;coly3[p3]+=val) coly3[p3]=coly1[p1]-10;repaint(); p1++;
梵塔Applet程序代码 colx1[p1]=colx3[p3];colx3[p3]=0; coly1[p1]=coly3[p3];coly3[p3]=0; colr1[p1]=colr3[p3];colr3[p3]=0; img1[p1] =img3[p3];img3[p3]=null; p3--; repaint(); try{Thread.sleep(speed);} catch(InterruptedException e){} break; case 2: for(;coly3[p3]<=coly2[p2]-10;coly3[p3]+=val) { } coly3[p3]=coly2[p2]-10;repaint(); p2++; colx2[p2]=colx3[p3];colx3[p3]=0; coly2[p2]=coly3[p3];coly3[p3]=0; colr2[p2]=colr3[p3];colr3[p3]=0; img2[p2] =img3[p3];img3[p3]=null; }//end move
梵塔Applet程序代码 public void paint(Graphics g) { offScreenGrahpics.drawImage(imgBack,0,0,this); for(int i=1;i<=p1;i++) offScreenGrahpics.drawImage(img1[i],colx1[i]-colr1[i],coly1[i]-5,this); } for(int i=1;i<=p2;i++) offScreenGrahpics.drawImage(img2[i],colx2[i]-colr2[i],coly2[i]-5,this); for(int i=1;i<=p3;i++) offScreenGrahpics.drawImage(img3[i],colx3[i]-colr3[i],coly3[i]-5,this); String[] str = {"Number+","Number-","Play","Stop","Speed+","Speed-","Renew"}; for(int i=1;i<=7;i++) offScreenGrahpics.setColor(buttonColor[i]); offScreenGrahpics.fill3DRect(BuStart+(i-1)*BuVal,BuLevel,w,h,s(state[i])); offScreenGrahpics.setColor(Color.blue); offScreenGrahpics.drawString(str[i-1],LaStart+(i-1)*LaVal,LaLevel); offScreenGrahpics.setColor(Color.red); offScreenGrahpics.drawRect(530,0,240,379); offScreenGrahpics.setColor(Color.black); offScreenGrahpics.fillRect(530,0,230,375); offScreenGrahpics.drawRect(535+4,2+10,230-14,379-20); offScreenGrahpics.drawString("(问题归约状态描述区)",590,190); offScreenGrahpics.drawString(" 梵塔难题的归约",590,90); g.drawImage(offScreenImage,0,0,this); }//end paint
hanoiApplet.htm文件 相应HTML文件如下: <!-- hanoiApplet.htm文件 --> <!-- hanoiApplet.htm文件 --> <html> <head> <title>hanio</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <table border=0 cellpadding=0 cellspacing=0 width="102%"> <tr> <td width="99%" height="22"> </td> </tr> <td width="99%" height="70"><applet code = "hanoiApplet.class" width="770" height="379" vspace="0" hspace="0" align="left"> </applet> </td> <tbody> <td width="99%" height="25"> </td> </tbody> </table> </body> </html>
8.7使用Applet将谓词公式化为子句集 实际上,客户端上运行的Applet也可以完成一些复杂的功能。本节中,将介绍如何开发将一个谓词公式化为子句集的Applet程序。 谓词公式和字句集是离散数学、人工智能以及有关逻辑推理方面书籍中的基本概念。基于谓词逻辑的推理系统中,需要将对实际问题表示和抽象的谓词公式化为字句的集合(子句集),以便对子句进行消解,从而完成相应的推理任务。 将一个谓词公式化为子句集一般要经过以下九个步骤: 消去蕴涵符号 减少否定符号的辖域 对变量标准化 消去存在量词 化为前束形 把母式化为合取范式 消去全称量词 消去连词符号∧ 更换变量名称
程序的设计思想 该Applet程序的功能主要包括: 提供键盘功能。其作用是编辑谓词公式,以作为程序的输入数据。 对输入的谓词公式进行语法检查,对不符合既定规范的公式进行提示,并强制修改。 按照化为子句集的九个步骤一次对输入的谓词功能进行处理,最后得到相应的子句集。 为简化程序的复杂性,对它进行如下的约定: 只能用一个字母作变量,例如A∨B是合法的,但是fx∨fy是非法的。 要把存在量词和全称量词与其对应的变元用小括号括起来。例如:(x)A,(x)A都是合法的,而 xA与xA是非法的。 程序不考虑算符的优先级,所以在输入的时候要显式地用括号来规定优先级,可用大、中、小括号,但一定要匹配,例如:A∧B∧C,要写成(A∧B)∧C或A∧(B∧C)。但(A∨B)∨C可以写成A∨B∨C。 最后得到的子句分别用中括号括起来。
Applet的启动界面
程序的实现 该程序是Applet程序,实现技术主要包括多线程技术、缓冲技术、Applet的编程技巧等。其代码见: Ch8.doc (P47-P109)
hornset.htm文件 以下是相应的HTML文件: <!--hornset.htm文件--> <html> <!--hornset.htm文件--> <html> <head> <title>testgu</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF" text="#000000"> <applet code="hornset.class" width="1200" height="461"> </applet> </body> </html> 编译hornset.java文件,将产生的hornset.class类,连同hornset.htm文件都放在D:\tomcat4\webapps\ROOT\ch8目录下。然后执行hornset.htm文件,在打开的页面中求默认公式的子句集,结果如图8.20。
求程序默认公式的子句集
8.7 小结 本章首先介绍了Applet及其基本结构,然后依次介绍了Applet类、Graphics类和多线程技术,最后给出了两个具体的应用实例。通过对本章的学习,读者应该掌握下列内容: Applet的基本结构 Applet类和Graphics类常用的方法 多线程技术 Applet应用程序开发方法
答疑联系信息 办公室电话:021-67792312 E-mail:taogong@dhu.edu.cn 办公室地址:2号学院楼216室