在这个专题中我们由浅入深地向大家介绍Java Servlet的基本特征、开发环境的配置以及Servlet的一些主要API类。
Servlet是一种独立于平台和协议的服务器端的Java应用程序,可以生成动态的Web页面
一、概述
Servlet是一种独立于平台和协议的服务器端的Java应用程序,可以生成动态的Web页面。
Servlet是位于Web 服务器内部的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,Servlet由Web服务器进行加载,该Web服务器必须包含支持Servlet的Java虚拟机。
Java Servlet 与 Applet 的比较:
相似之处:
* 它们不是独立的应用程序,没有main()方法。
* 它们不是由用户或程序员调用,而是由另外一个应用程序(容器)调用。
* 它们都有一个生存周期,包含init()和destroy()方法。
不同之处:
* Applet具有很好的图形界面(AWT),与浏览器一起,在客户端运行。
* Servlet 则没有图形界面,运行在服务器端。
Java Servlet 与 CGI(Common Gateway Interface) 的比较:
与传统的CGI和许多其他类似CGI的技术相比,Java Servlet具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资。在未来的技术发展过程中,Servlet有可能彻底取代CGI。
* 高效
在传统的CGI中,每个请求都要启动一个新的进程,如果CGI程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。
在传统CGI中,如果有N个并发的对同一CGI程序的请求,则该CGI程序的代码在内存中重复装载了N次;而对于Servlet,处理请求的是N个线程,只需要一份Servlet类代码。在性能优化方面,Servlet也比CGI有着更多的选择。
* 方便
Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。
* 功能强大
在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。
* 可移植性好
Servlet用Java编写,Servlet API具有完善的标准。因此,为IPlanet Enterprise Server写的Servlet无需任何实质上的改动即可移植到Apache、Microsoft IIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。
* 节省投资
不仅有许多廉价甚至免费的Web服务器可供个人或小规模网站使用,而且对于现有的服务器,如果它不支持Servlet的话,要加上这部分功能也往往是免费的(或只需要极少的投资)。
Java Servlet 与 JSP(JavaServer Pages) 的比较:
JavaServer Pages(JSP)是一种实现普通静态HTML和动态HTML混合编码的技术,JSP并没有增加任何本质上不能用Servlet实现的功能。但是,在JSP中编写静态HTML更加方便,不必再用println语句来输出每一行HTML代码。更重要的是,借助内容和外观的分离,页面制作中不同性质的任务可以方便地分开:比如,由页面设计者进行HTML设计,同时留出供Servlet程序员插入动态内容的空间。
Java Servlet API 2.2 简介
Java Servlet API 2.2 的类和接口组成两个Java 包,即:javax.servlet 和 javax.servlet.http(还包括javax.servlet.jsp包,不在本篇文章讨论范围之内)。
javax.servlet 包提供了控制 Servlet 生命周期所必需的 Servlet 接口,是编写 Servlet 时必须要实现的。
javax.servlet.http 包提供了从Servlet 接口派生出的专门用于处理 HTTP 请求的抽象类和一般的工具类。所有的Servlet 对象都要实现Servlet 接口,大多数情况下是作为已经实现了Servlet 接口的javax.servlet.GenericServlet 和 javax.servlet.http.HttpServlet 这两个抽象类的子类来间接实现Servlet 接口。
javax.servlet 包定义的类和接口:
interface RequestDispatcher
//定义一种对象,用于从客户接受请求,并将请求发送到服务器上任何指定的资源,如一个Servlet 、JSP 或 HTML 文件。
interface Servlet
//定义了所有 Servlet 必须实现的方法。
interface ServletConfig
//定义Servlet config 对象,由Servlet 引擎用在 Servlet 初始化时,向 Servlet 传递信息。
interface ServletContext
//定义了一系列方法,以便Servlet与其运行的环境通信。
interface ServletRequest
//定义了用于向Servlet传递客户请求信息的对象。
interface ServletResponse
//定义了一个对象,由Servlet用于向客户发送响应。
interface SingleThreadModel
//用于保证Servlet在任一时刻,只处理一个请求。
class GenericServlet
//继承Servlet接口,定义了一个通用的,与协议无关的Servlet。
class ServletInputStream
//定义了一个输入流,用于由Servlet从中读取客户请求的二进制数据。
class ServletOutputStream
//定义了一个输出流,用于由Servlet向客户发送二进制数据。
class ServletException
//定义了一个当Servlet遇到问题时可以抛出的异常。
class UnavailableException
//定义了一种异常,用于由Servlet指明它永远或暂时不可用。
javax.servlet.http 包定义的类和接口:
interface HttpServletRequest
//继承了ServletRequest 接口,为HTTPServlet 提供请求信息。
interface HttpServletResponse
//继承了ServletResponse 接口,为HTTPServlet 输出响应信息提供支持。
interface HttpSession
//为维护 HTTP 用户的会话状态提供支持。
interface HttpSessionBindingListener
//使得某对象在加入一个会话或从会话中删除时能够得到通知。
interface HttpSessionContext
//由Servlet 2.1 定义,该对象在新版本已不被支持。
class Cookie
//用在Servlet 中使用Cookie 技术
class HttpServlet
//定义了一个抽象类,继承 GenericServlet 抽象类,应被 HTTPServlet 继承。
class HttpSessionBindingEvent
//定义了一种对象,当某一个实现了HttpSessionBindingListener接口的对象被加入会话或从会//话中删除时,会收到该类对象的一个句柄
class HttpUtils
//提供了一系列便于编写HTTPServlet 的方法。
下面主要介绍javax.servlet.http提供的HTTP Servlet应用编程接口。
HTTP Servlet 使用一个 HTML 表格来发送和接收数据。要创建一个 HTTP Servlet,请扩展 HttpServlet 类, 该类是用专门的方法来处理 HTML 表格的 GenericServlet 的一个子类。 HTML 表单是由 <FORM> 和 </FORM> 标记定义的。表单中典型地包含输入字段(如文本输入字段、复选框、单选按钮和选择列表)和用于提交数据的按钮。当提交信息时,它们还指定服务器应执行哪一个Servlet(或其它的程序)。 HttpServlet 类包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是继承的。
(1) init() 方法
在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。
缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法来覆盖它,典型的是管理服务器端资源。 例如,可能编写一个定制 init() 来只用于一次装入 GIF 图像,改进 Servlet 返回 GIF 图像和含有多个客户机请求的性能。另一个示例是初始化数据库连接。缺省的 init() 方法设置了 Servlet 的初始化参数,并用它的 ServletConfig 对象参数来启动配置, 因此所有覆盖 init() 方法的 Servlet 应调用 super.init() 以确保仍然执行这些任务。在调用 service() 方法之前,应确保已完成了 init() 方法。
(2) service() 方法
service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。例如, 如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet() 。Servlet 应该为 Servlet 支持的 HTTP 方法覆盖 do 功能。因为 HttpServlet.service() 方法会检查请求方法是否调用了适当的处理方法,不必要覆盖 service() 方法。只需覆盖相应的 do 方法就可以了。
当一个客户通过HTML 表单发出一个HTTP POST请求时,doPost()方法被调用。与POST请求相关的参数作为一个单独的HTTP 请求从浏览器发送到服务器。当需要修改服务器端的数据时,应该使用doPost()方法。
当一个客户通过HTML 表单发出一个HTTP GET请求或直接请求一个URL时,doGet()方法被调用。与GET请求相关的参数添加到URL的后面,并与这个请求一起发送。当不会修改服务器端的数据时,应该使用doGet()方法。
Servlet的响应可以是下列几种类型:
一个输出流,浏览器根据它的内容类型(如text/HTML)进行解释。
一个HTTP错误响应, 重定向到另一个URL、servlet、JSP。
(3) destroy() 方法
destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。例如,如果 Servlet 在运行时会累计统计数据,则可以编写一个 destroy() 方法,该方法用于在未装入 Servlet 时将统计数字保存在文件中。另一个示例是关闭数据库连接。
当服务器卸装 Servlet 时,将在所有 service() 方法调用完成后,或在指定的时间间隔过后调用 destroy() 方法。一个Servlet 在运行service() 方法时可能会产生其它的线程,因此请确认在调用 destroy() 方法时,这些线程已终止或完成。
(4) GetServletConfig()方法
GetServletConfig()方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和ServletContext。ServletContext 接口提供有关servlet 的环境信息。
(5) GetServletInfo()方法
GetServletInfo()方法是一个可选的方法,它提供有关servlet 的信息,如作者、版本、版权。
当服务器调用sevlet 的Service()、doGet()和doPost()这三个方法时,均需要 "请求"和"响应"对象作为参数。"请求"对象提供有关请求的信息,而"响应"对象提供了一个将响应信息返回给浏览器的一个通信途径。
javax.servlet 软件包中的相关类为ServletResponse和ServletRequest,而javax.servlet.http 软件包中的相关类为HttpServletRequest 和 HttpServletResponse。
Servlet 通过这些对象与服务器通信并最终与客户机通信。Servlet 能通过调用"请求"对象的方法获知客户机环境,服务器环境的信息和所有由客户机提供的信息。Servlet 可以调用"响应"对象的方法发送响应,该响应是准备发回客户机的。
进行Servlet开发所需要的基本环境是JSDK以及一个支持Servlet的Web服务器
编写Servlet所需要的开发环境
进行Servlet开发所需要的基本环境是JSDK以及一个支持Servlet的Web服务器。
1.JSDK(Java Servlet Development Kit)
JSDK包含了编译Servlet应用程序所需要的Java类库以及相关的文档。对于利用Java 1.1进行开发的用户,必须安装JSDK。JSDK已经被集成进Java 1.2 Beta版中,如果利用Java 1.2或以上版本进行开发,则不必安装JSDK。
JSDK可以在Javasoft公司的站点免费下载,其地址是: http://www.sun.com/software/jwebserver/redirect.html
2.支持Servlet的Web服务器
Servlet需要运行在支持Servlet的Web服务器上。目前支持Servlet的Web服务器SUN公司的JSWDK1.0.1。如果现有的Web服务器不支持Servlet,则可以利用一些第三方厂商的服务器增加件(add-ons)来使Web服务器支持Servlet,这其中Live Software公司(http://www.livesoftware.com)提供了一种称为JRun的产品,通过安装JRun的相应版本,可以使Microsoft IIS和Netscape Web Server支持Servlet。
开发Servlet的过程
下面举一个简单的Servlet 例子来说明开发Servlet的过程。
1.编写Servlet代码
Java Servlet API是一个标准的Java扩展程序包,包含两个Package∶javax.servlet和javax.servlet.http。对于想开发基于客户自定义协议的开发者,应该使用javax.servlet包中的类与界面;对于仅利用HTTP协议与客户端进行交互的开发者,则只需要使用javax.servlet.http包中的类与界面进行开发即可。
下面是一个servlet的程序代码(RequestInfoExample.java)∶ import java.io.*;
import java.servlet.*;
import javax.servlet.*;
public class RequestInfoExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Request Information Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Request Information Example</h3>");
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request, HttpServletResponse res)
throws IOException, ServletException
{
doGet(request, response);
}
}
该servlet实现如下功能∶当用户通过浏览器访问该servlet时,该servlet向客户端浏览器返回一个HTML页面∶ ------------------------------------------------
Request Information Example
Method: GET
Request URI: /examples/servlet/RequestInfoExample
Protocol: HTTP/1.1
Path Info: null
Remote Address: 127.0.0.1
--------------------------------------------------
有关servlet程序说明∶
* 基于HTTP协议的servlet必须引入javax.servlet和javax.servlet.http包;
* HelloServlet从类HttpServlet派生,HttpServlet是GenericServlet的一个派生类,通过 GenericServlet实现了Servlet界面。HttpServlet为基于HTTP协议的servlet提供了基本的支持;
* HttpServletRequest对象包含了客户端请求的信息,可以通过该参数取得客户端的一些信息(例如IP地址、浏览器类型等)以及HTTP请求类型(例如GET、HEAD、POST、PUT等);HttpServletResponse对象用于完成Servlet与客户端的交互,通过调用HttpServletResponse.getOutputStream()客户取得向客户端进行输出的输出流,向客户端发送HTML页面。
* 编写了doGet方法,对于HTML POST 请求,调用Servlet 的doPost()方法。
2.编译Servlet代码
利用JDK 1.2.2 对Servlet代码进行编译(假设Web服务器采用jswdk-1.0.1),其命令行为:
c:\> javac -d C:\jswdk-1.0.1\examples\WEB-INF\servlets HelloServlet.java
进行编译时必须确保HelloServlet.java 文件拷贝到目录C:\jswdk-1.0.1\examples\WEB- INF\servlets 下面。
3.测试Servlet
现在可以对HelloServlet进行测试了,打开浏览器,键入:
http://localhost:8080/examples/servlet/RequestInfoExample
其中localhost是安装有jswdk-1.0.1的机器,8080是端口号。
希尔排序法基本思想是:取一个间隔,将长序列分成若干短的子序列,对每个子序列进行直插排序;然后逐渐缩小间隔,重复以上过程,直到间隔为1
前面我们学习了两种插入排序法,但当要排序的数组长度越长并且数值越不成顺序,比较和交换的次数就越多,效率越低。因此D.L.Shell在1959年提出了缩小增量排序法(又叫希尔排序法),基本思想是:取一个间隔,将长序列分成若干短的子序列,对每个子序列进行直插排序;然后逐渐缩小间隔,重复以上过程,直到间隔为1。可以看到这种算法,较好的克服了直接插入排序法的不足。
下面是示例:8 7 4 3 6 1 //是要排序的数值,我们以一半的长度为间隔3
3 7 4 8 6 1 //第一次,取得3,小于前面的8,交换位置
3 6 4 8 7 1 //第二次,取得6,小于前面的7,交换位置
3 6 1 8 7 4 //第三次,取得1,小于前面的4,交换位置
1 6 3 4 7 8 //第四次,再缩小间隔,为2,取得1小于3,交换位置,取得7,大于前面的3,不变;取得8大于6,不变,取得4小于8,交换位置
1 3 4 6 7 8 //第五次,再缩小间隔,为1,取得6,大于1,不变;取得3小于6,交换位置;取得4,小于6,交换位置;取得7,大于前面的6,不变;取得8 ,大于7,不变
以下是代码:void paixu( ) //用希尔排序法,
{
int N=13;// N为前后纪录位置的增量
for (int Z= N/2; Z; Z = Z/2)//每次缩小增量
for (int i = Z; i < N; i++)//从增两大小开始比较
{
int temp = apai[i]; //将后一个备份
for (int j = i; j >= Z && temp < a[j - Z]; j -= Z) //与他在同一个子序列的数一个个的较
{
a[j] = a[j -Z]; //如果小于,就交换
}//end for
a[j] = temp; //找到合适的插入点,放入其中
}//end for
}//end
我们再来看最后一种关于数组的排序方法,就是快速排序法,它是目前最快的一种排序的方法.它的基本思想是:通过一趟排序将待排序的记录分割为独立的两部分,其中一部分记录的数值均比另一部分记录的数值小,然后继续分别对这两部分进行排序,直到整个序列有序为止.
具体做法: 任取待排序列的某个记录(我们可以取第一个数)作为基准,按照该数值大小,将整个序列分成两个序列——左侧的所有记录的数值都比基准小(或者相等),右侧的都比基准大,基准则放在两个子序列之间,显然这时基准放在了最后应该放置的位置。分别对左右子序列重复上面的过程,直到最后所有的记录都放在相应的位置。
示例如下:7 8 4 3 6 1 //是要排序的数值
1 8 4 3 6 //第一次,取得7,作为基准,1为right值,7>1,交换位置
1 4 3 6 8 //第二次, 8为left值,7<8,放到最后;
1 4 3 6 8 //第三次,left取得4,小于7,放到前面,
1 4 6 3 8 //第四次,right取6,小于7,放到前面
1 4 6 3 8 //第五次,left=right=3,小于7,放到前面,
1 4 6 3 7 8 //7放入合适位置,第一趟排序完成
//后面,在以1为基准排序
……
//直到成功
代码如下:void paixu(int a[],int low,int high;)//用快速排序法
{
// low, high表示扫描的范围
int pivot;//存放中心索引及其值的局部变量
int scanup,scandown,mid;//用于扫描的索引
if (high-low<=0) //如果数组中的元素少于两个,则返回
return;
else
if(high-low==1) //如果有两个元素,对其进行比较
{
if(apai[high]<apai[low]) //如果后一个比前一个小,
Swap(apai[low],apai[high]);//那么交换位置
return;
}//end if
mid=(low+high)/2;//取得中心索引
pivot=apai[mid];//将中间索引的值,赋给pivot
Swap(apai[mid],apai[low]);//交换pivot及低端元素的值
Scanup=low+1;
Scandown=high;//初始化扫描索引scanup和scandown
do{
//从低端子表向上扫描,当scanup进入高端子表或遇到大于pivot的元素时结束.
while(scanup<=scandown && apai[scanup]<=pivot)
scanup++;
//从高端子表向下扫描,当scandown遇到小于或等于pivot的元素时结束
while(piovt<apai[scandown])
scandown--;
//如果两个索引还在各自的子表中,则表示两个元素错位,将两个元素换位
if(scanup<scandown)
Swap(apai[scanup],apai[scandown]);
}while(scanup<scandown);
//将pivot拷贝到scandown位置,分开两个子表
apai[low]=apai[scandown];
apai[scandown]=pivot;
//如果低端子表(low至scandown-1)有2个或更多个元素,则进行递归调用
if(low<scandown-1)
paixu(apai,low,scandown-1);
//如果高端子表(scandown+1至high) 有2个或更多个元素,则进行递归调用
if(scandown+1<high)
paixu(apai, scandown+1, high);
}
关于排序的问题已经够多了,就到这里吧,如果大家有兴趣,可以看已看这方面的书.
HttpServlet 是从GenericServlet 继承而来,因此它具有GenericServlet 类似的方法和对象,是我们使用Servlet编程经常用到的包,它支持HTTP 的post 和 get 等方法。
编程思路:下面的例子,运行结果是输出简单地返回客户发送给服务器的请求行和头部信息,以及一些可访问的HTTP 信息等。
SnoopServlet.java 的源代码如下:import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
public class SnoopServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
response.setContentType("text/plain");
out.println("Snoop Servlet");
out.println();
out.println("Servlet init parameters:");
Enumeration e = getInitParameterNames();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
String value = getInitParameter(key);
out.println(" " + key + " = " + value);
}
out.println();
out.println("Context init parameters:");
ServletContext context = getServletContext();
Enumeration enum = context.getInitParameterNames();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
Object value = context.getInitParameter(key);
out.println(" " + key + " = " + value);
}
out.println();
out.println("Context attributes:");
enum = context.getAttributeNames();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
Object value = context.getAttribute(key);
out.println(" " + key + " = " + value);
}
out.println();
out.println("Request attributes:");
e = request.getAttributeNames();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
Object value = request.getAttribute(key);
out.println(" " + key + " = " + value);
}
out.println();
out.println("Servlet Name: " + getServletName());
out.println("Protocol: " + request.getProtocol());
out.println("Scheme: " + request.getScheme());
out.println("Server Name: " + request.getServerName());
out.println("Server Port: " + request.getServerPort());
out.println("Server Info: " + context.getServerInfo());
out.println("Remote Addr: " + request.getRemoteAddr());
out.println("Remote Host: " + request.getRemoteHost());
out.println("Character Encoding: " + request.getCharacterEncoding());
out.println("Content Length: " + request.getContentLength());
out.println("Content Type: "+ request.getContentType());
out.println("Locale: "+ request.getLocale());
out.println("Default Response Buffer: "+ response.getBufferSize());
out.println();
out.println("Parameter names in this request:");
e = request.getParameterNames();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
String[] values = request.getParameterValues(key);
out.print(" " + key + " = ");
for(int i = 0; i < values.length; i++) {
out.print(values[i] + " ");
}
out.println();
}
out.println();
out.println("Headers in this request:");
e = request.getHeaderNames();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
String value = request.getHeader(key);
out.println(" " + key + ": " + value);
}
out.println();
out.println("Cookies in this request:");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
out.println(" " + cookie.getName() + " = "+ cookie.getValue());
}
}
out.println();
out.println("Request Is Secure: " + request.isSecure());
out.println("Auth Type: " + request.getAuthType());
out.println("HTTP Method: " + request.getMethod());
out.println("Remote User: " + request.getRemoteUser());
out.println("Request URI: " + request.getRequestURI());
out.println("Context Path: " + request.getContextPath());
out.println("Servlet Path: " + request.getServletPath());
out.println("Path Info: " + request.getPathInfo());
out.println("Path Trans: " + request.getPathTranslated());
out.println("Query String: " + request.getQueryString());
out.println();
HttpSession session = request.getSession();
out.println("Requested Session Id: " +
request.getRequestedSessionId());
out.println("Current Session Id: " + session.getId());
out.println("Session Created Time: " + session.getCreationTime());
out.println("Session Last Accessed Time: " +session.getLastAccessedTime());
out.println("Session Max Inactive Interval Seconds: " + session.getMaxInactiveInterval());
out.println();
out.println("Session values: ");
Enumeration names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
out.println(" " + name + " = " + session.getAttribute(name));
}
}
}
编程技巧说明:
程序输出Init Parameters(初始化参数)、Attribute names in this request、Parameter names in this request、Headers in this request、Cookies in this request 和 Session Information等信息。
方法getRequestURI返回的对象URI 是作为URL 的一部分,是去掉URL 中用于指定机器的那部分;方法getPathInfo 返回的字符串是客户向Servlet 传送的各种选项,这些选项是跟在Servlet 的URL 之后的,方法getPathTranslated 返回的字符串是Servlet 的自己的绝对路径名,SnoopServlet.class 文件位置是C:\jswdk-1.0.1\examples\WEB-INF\servlets\SnoopServlet.class,则方法getPathTranslated 返回的字符串值就是它。
在浏览器中输入如下的地址:http://localhost:8080/examples/servlet/SnoopServlet
则会输出结果。
Cookie 是一小块可以嵌入HTTP 请求和响应中的数据,它在服务器上产生,并作为响应头域的一部分返回用户。浏览器收到包含Cookie 的响应后,会把Cookie 的内容用“关键字/值” 对的形式写入到一个客户端专为存放Cookie 的文本文件中。浏览器会把Cookie 及随后产生的请求发给相同的服务器,服务器可以再次读取Cookie 中存Cookie 可以进行有效期设置,过期的Cookie 不会发送给服务器。
Servlet API 提供了一个Cookie 类,封装了对Cookie 的一些操作。Servlet 可以创建一个新的Cookie,设置它的关键字、值及有效期等属性,然后把Cookie 设置在HttpServletResponse 对象中发回浏览器,还可以从HttpServletRequest 对象中获取Cookie。
编程思路:Cookie 在实际的Servlet 编程中是很广泛应用,下面是一个从Servlet 中获取Cookie 信息的例子。
ShowCookies.java 的源代码如下:import javax.servlet.*;
import javax.servlet.http.*;
/**
* <p>This is a simple servlet that displays all of the
* Cookies present in the request
*/
public class ShowCookies extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
{
// Set the content type of the response
resp.setContentType("text/html;charset=gb2312");
// Get the PrintWriter to write the response
java.io.PrintWriter out = resp.getWriter();
// Get an array containing all of the cookies
Cookie cookies[] = req.getCookies();
// Write the page header
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet Cookie Information</title>");
out.println("</head>");
out.println("<body>");
if ((cookies == null) || (cookies.length == 0)) {
out.println("没有 cookies ");
}
else {
out.println("<center><h1>响应消息中的Cookies 信息 </h1>");
// Display a table with all of the info
out.println("<table border>");
out.println("<tr><th>Name</th><th>Value</th>" + "<th>Comment</th><th>Max Age</th></tr>");
for (int i = 0; i < cookies.length; i++) {
Cookie c = cookies[i];
out.println("<tr><td>" + c.getName() + "</td><td>" +
c.getValue() + "</td><td>" + c.getComment() + "</td><td>" + c.getMaxAge() + "</td></tr>");
}
out.println("</table></center>");
}
// Wrap up
out.println("</body>");
out.println("</html>");
out.flush();
}
/**
* <p>Initialize the servlet. This is called once when the
* servlet is loaded. It is guaranteed to complete before any
* requests are made to the servlet
* @param cfg Servlet configuration information
*/
public void init(ServletConfig cfg)
throws ServletException
{
super.init(cfg);
}
/**
* <p>Destroy the servlet. This is called once when the servlet
* is unloaded.
*/
public void destroy()
{
super.destroy();
}
}
注意:Cookie 进行服务器端与客户端的双向交流,所以它涉及到安全性问题。
使用Java Servlet API 进行会话管理
javax.servlet.http.HttpSession 接口封装了HTTP 会话的细节,该会话与一段时间内特定的Web 客户对Web 服务器的多个请求相关。管理会话数据主要涉及到3个方面:会话交换、会话重定位和会话持久性,只有实现了java.io.Serializable 接口的数据对象才能够被交换、重定位和保持。这个接口主要是让对象具有序列化的能力,它可以将对象的状态信息写入任意的输出流中如:文件、网络连接等。
编程思路:下面是实现一个简单在商场购物的例子,当用户选购商品(糖果、收音机和练习簿)放入购物袋中,保存选购的商品信息。
ShowBuy.java 的源代码如下:import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class ShowBuy extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, java.io.IOException
{
String[] item={"糖果","收音机","练习簿"};
//获取会话对象
HttpSession session=req.getSession(true);
//获取选择的商品数目
Integer itemCount=(Interger) session.getValue("itemCount");
//如果没放入商品则数目为0
if (itemCount==null){
itemCount=new Integer(0);
}
// Set the content type of the response
res.setContentType("text/html;charset=gb2312");
PrintWriter out=res.getWriter();
//取得POST上来的表单信息
String[] itemsSelected;
String itemName;
itemsSelected=req.getParameterValues("item");
//将选中的商品放入会话对象
if(itemsSelected !=null){
for(int i=0;i<itemsSelected.length;i++){
itemName=itemsSelected[i];
itemCount=new Integer(itemCount.intValue()+1);
session.putValue("Item" + itemCount,itemName);
//将商品名称定义为ItemX
session.putValue("itemCount",itemCount);
//将商品数量放入会话对象
}
}
// Write the page header
out.println("<html>");
out.println("<head>");
out.println("<title>购物袋的内容</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>你放在购物袋中的商品是: </h1></center>");
//将购物袋的内容写入页面
for (int i = 1; i < itemCount.intValue(); i++) {
String item =(String) session.getValue("Item"+i);
//取出商品名称
out.println(items[Integer.parseInt(item)]);
out.println("<BR>");
}
// Wrap up
out.println("</body>");
out.println("</html>");
out.close();
}
}
客户端的ShowBuy.html 的源代码如下:<HTML>
<HEAD>
<TITLE>购物袋的实例 </TITLE>
</HEAD>
<BODY>
<CENTER><H1>百货商场</H1></CENTER>
<HR>
<FORM ACTION='servlet/ShowBuy" METHOD="POST">
选购商品
<p><INPUT TYPE="Checkbox" NAME="item" VALUE="0">
第一种:糖果</p>
<p><INPUT TYPE="Checkbox" NAME="item" VALUE="1">
第二种:收音机</p>
<p><INPUT TYPE="Checkbox" NAME="item" VALUE="2">
第三种:练习簿</p>
<HR>
<INPUT TYPE="Submit" NAME="bt_submit" VALUE="加入购物袋">
</FORM>
</BODY>
</HTML>
编程技巧说明:
在Servlet 中进行会话管理时,首先要获得会话对象。HttpServletRequest.getSession()对象返回与请求相关的当前HttpSession 对象,并且当该对象不存在时就新创建一个对象;HttpServletRequest.getSession(true)实现相同的功能。如果参数是false,当不存在会话对象时,将返回一个null 值。//获取会话对象
HttpSession session=req.getSession(true);
//获取选择的商品数目
Integer itemCount=(Interger) session.getValue("itemCount");
具体操作时,当用户选择商品后,单击“加入购物袋"按钮,Servlet 输出用户选择的商品。
Java Applet 编程技巧实例专辑
Java最初奉献给世人的就是Applet,随即它吸引了全世界的目光,Applet运行于浏览器上,可以生成生动美丽的页面,进行友好的人机交互,同时还能处理图像、声音、动画等多媒体数据。Applet在Java的成长过程中起到不可估量的作用,到今天Applet依然是Java程序设计最吸引的人之一。在本期专题中我将向读者介绍Applet编程的一些技巧。
Java Applet 是用Java 语言编写的一些小应用程序,这些程序是直接嵌入到页面中,由支持Java的浏览器(IE 或 Nescape)解释执行能够产生特殊效果的程序。它可以大大提高Web页面的交互能力和动态执行能力。包含Applet的网页被称为Java-powered页,可以称其为Java支持的网页。
当用户访问这样的网页时,Applet被下载到用户的计算机上执行,但前提是用户使用的是支持Java的网络浏览器。由于Applet是在用户的计算机上执行的,所以它的执行速度不受网络带宽或者Modem存取速度的限制,用户可以更好地欣赏网页上Applet产生的多媒体效果。
Applet 小应用程序的实现主要依靠java.applet 包中的Applet类。与一般的应用程序不同,Applet应用程序必须嵌入在HTML页面中,才能得到解释执行;同时Applet可以从Web页面中获得参数,并和Web页面进行交互。
含有Applet的网页的HTML文件代码中必须带有<applet>和</applet>这样一对标记,当支持Java的网络浏览器遇到这对标记时,就将下载相应的小程序代码并在本地计算机上执行该Applet小程序。
Applet是一种Java的小程序,它通过使用该Applet的HTML文件,由支持Java的网页浏览器下载运行。也可以通过java开发工具的appletviewer来运行。Applet 程序离不开使用它的HTML文件。这个HTML文件中关于Applet的信息至少应包含以下三点:
1)字节码文件名(编译后的Java文件,以.class为后缀)
2)字节码文件的地址
3)在网页上显示Applet的方式。
一个HTML文件增加Applet有关的内容只是使网页更加富有生气,如添加声音、动画等这些吸引人的特征,它并不会改变HTML文件中与Applet无关的元素。
(一) Applet程序开发步骤
Applet程序开发主要步骤如下:
1)选用EDIT或Windows Notepad等工具作为编辑器建立Java Applet源程序。
2)把Applet的源程序转换为字节码文件。
3)编制使用class 的HTML文件。在HTML文件内放入必要的<APPLET>语句。
下面举一个最简单的HelloWorld 例子来说明Applet程序的开发过程:
(1) 编辑Applet 的java源文件
创建文件夹C:\ghq,在该文件夹下建立 HelloWorld.java
文件的源代码如下:import java.awt.*;
import java.applet.*;
public class HelloWorld extends Applet //继承Appelet类,这是Appelet Java程序的特点
{
public void paint(Graphics g )
{
g.drawString("Hello World!",5,35);
}
}
保存上述程序在C:\ghq\HelloWorld.java文件里。
(2)编译Applet
编译HelloWorld.java源文件可使用如下JDK命令:C:\ghq\>javac HelloWorld.java<Enter>
注意:如果编写的源程序违反了Java编程语言的语法规则,Java编译器将在屏幕上显示语法错误提示信息。源文件中必须不含任何语法错误,Java编译器才能成功地把源程序转换为appletviewer和浏览器能够执行的字节码程序。
成功地编译Java applet之后生成响应的字节码文件HelloWorld.class的文件。用资源管理器或DIR命令列出目录列表,将会发现目录C:\ghq中多了一个名为HelloWorld.class的文件。
(3)创建HTML文件
在运行创建的HelloWorld.class 之前,还需创建一个HTML文件,appletviewer或浏览器将通过该文件访问创建的Applet。为运行HelloWorld.class, 需要创建包含如下HTML语句的名为HelloWorld.html的文件。<HTML>
<TITLE>HelloWorld! Applet</TITLE>
<APPLET
CODE="JavaWorld.class"
WIDTH=200
HEIGHT=100>
</APPLET>
</HTML>
本例中,<APPLET>语句指明该Applet字节码类文件名和以像素为单位的窗口的尺寸。虽然这里HTML文件使用的文件名为HelloWorld.HTML,它对应于HelloWorld.java的名字,但这种对应关系不是必须的,可以用其他的任何名字(比如说Ghq.HTML)命名该HTML文件。但是使文件名保持一种对应关系可给文件的管理带来方便。
(4)执行 HelloWorld.html
如果用appletviewer运行HelloWorld.html,需输入如下的命令行:C:\ghq\>appletviewer JavaWorld.html<ENTER>
可以看出,该命令启动了appletviewer并指明了HTML文件,该HTML文件中包含对应于HelloWorld 的<APPLET>语句。
如果用浏览器运行HelloWorld Applet,需在浏览器的地址栏中输入HTML文件URL地址。
至此,一个Applet程序的开发运行整个过程结束了(包括java源文件、编译的class文件、html文件以及用appletviewer或用浏览器运行)。
(二) Applet类
Applet类是所有Applet应用的基类,所有的Java小应用程序都必须继承该类。如下所示。import java. applet.*;
public class OurApplet extends Applet
{
......
......
}
Applet类的构造函数只有一种,即:public Applet()
Applet实现了很多基本的方法,下面列出了Applet类中常用方法和用途。public final void setStub(AppletStub stub)
//设置Applet的stub.stub是Java和C之间转换参数并返回值的代码位,它是由系统自动设定的。
public boolean isActive();// 判断一个Applet是否处于活动状态。
public URL getDocumentBase();// 检索表示该Applet运行的文件目录的对象。
public URL getCodeBase();// 获取该Applet 代码的URL地址。
public String getParameter(String name);// 获取该Applet 由name指定参数的值。
public AppletContext getAppletContext();// 返回浏览器或小应用程序观察器。
public void resize(int width,int height);// 调整Applet运行的窗口尺寸。
public void resize(Dimension d);// 调整Applet运行的窗口尺寸。
public void showStatus(String msg);// 在浏览器的状态条中显示指定的信息。
public Image getImage(URL url); // 按url指定的地址装入图象。
public Image getImage(URL url,String name);// 按url指定的地址和文件名加载图像。
public AudioClip getAudioClip(URL url);// 按url指定的地址获取声音文件。
public AudioClip getAudioClip(URL url, String name);// 按url指定的地址和文件名获取声音。
public String getAppletInfo();// 返回Applet应用有关的作者、版本和版权方面的信息;
public String[][] getParameterInfo();
// 返回描述Applet参数的字符串数组,该数组通常包含三个字符串: 参数名、该参数所需值的类型和该参数的说明。
public void play(URL url);// 加载并播放一个url指定的音频剪辑。
public void destroy();//撤消Applet及其所占用的资源。若该Applet是活动的,则先终止该Applet的运行。
(1) Applet运行状态控制基本方法
Applet类中的四种基本方法用来控制其运行状态:init()、start()、stop()、destroy()
init()方法
这个方法主要是为Applet的正常运行做一些初始化工作。当一个Applet被系统调用时,系统首先调用的就是该方法。通常可以在该方法中完成从网页向Applet传递参数,添加用户界面的基本组件等操作。
start()方法
系统在调用完init()方法之后,将自动调用start()方法。而且,每当用户离开包含该Applet的主页后又再返回时,系统又会再执行一遍start()方法。这就意味着start()方法可以被多次执行,而不像init()方法。因此,可把只希望执行一遍的代码放在init()方法中。可以在start()方法中开始一个线程,如继续一个动画、声音等。
stop()方法
这个方法在用户离开Applet所在页面时执行,因此,它也是可以被多次执行的。它使你可以在用户并不注意Applet的时候,停止一些耗用系统资源的工作以免影响系统的运行速度,且并不需要人为地去调用该方法。如果Applet中不包含动画、声音等程序,通常也不必实现该方法。
destroy()方法
与对象的finalize()方法不同,Java在浏览器关闭的时候才调用该方法。Applet是嵌在HTML文件中的,所以destroty()方法不关心何时Applet被关闭,它在浏览器关闭的时候自动执行。在destroy()方法中一般可以要求收回占用的非内存独立资源。(如果在Applet仍在运行时浏览器被关闭,系统将先执行stop()方法,再执行destroy()方法。
(2) Applet应用的有关参数说明
利用Applet来接收从HTML中传递过来的参数,下面对这些参数作一简单说明:
* CODE标志
CODE标志指定Applet的类名;WIDTH和HEIGHT标志指定Applet窗口的像素尺寸。在APPLET语句里还可使用其他一些标志。
* CODEBASE 标志
CODEBASE标志指定Applet的URL地址。Applet的通用资源定位地址URL,它可以是绝对地址 ,如www.sun.com。也可以是相对于当前HTML所在目录的相对地址,如/AppletPath/Name。如果HTML文件不指定CODEBASE 标志,浏览器将使用和HTML文件相同的URL。
* ALT 标志
虽然Java在WWW上很受欢迎,但并非所有浏览器都对其提供支持。如果某浏览器无法运行Java Applet,那么它在遇到APPLET语句时将显示ALT标志指定的文本信息。
* ALIGN 标志
ALIGN标志可用来控制把Applet窗口显示在HTML文档窗口的什么位置。与HTML<LMG>语句一样,ALIGN标志指定的值可以是TOP、MIDDLE或BOTTOM。
* VSPACE与HSPACE 标志
VSPACE和HSPACE标志指定浏览器显示在Applet窗口周围的水平和竖直空白条的尺寸,单位为像素。如下例使用该标志在Applet窗口之上和之下各留出50像素的空白,在其左和其右各留出25像素的空白:
* NAME 标志
NAME标志把指定的名字赋予Applet的当前实例。当浏览器同时运行两个或多个Applet时,各Applet可通过名字相互引用或交换信息。如果忽略NAME标志,Applet的名字将对应于其类名。
* PARAM 标志
通用性是程序设计所追求的目标之一。使用户或者程序员能很方便地使用同一个Applet完成不同的任务是通用性的具体表现。从HTML文件获取信息是提高Applet通用性的一条有效途径。
假设编制了一个把某公司的名字在屏幕上卷动的Applet。为了使该Applet更加通用,则可以使该Applet从HTML文件获取需要卷动的文本信息。这样,若想显示另一个公司的名字,用不着修改Java Applet本身,只需修改HTML文件即可。
PARAM 标志可用来在HTML文件里指定参数,格式如下所示:
PARAM Name="name" Value="Liter"
Java Applet可调用getParameter方法获取HTML文件里设置的参数值。
[文章导读]显示文字是Java中最基本的功能,使用非常简单的方式来支持文字的显示
显示文字是Java中最基本的功能,使用非常简单的方式来支持文字的显示,只要使用类Graphics中的drawString()函数就能实现。我们来看最简单的ghq例子: //ghq.java
import java.awt.*;
import java.applet.*;
public class ghq extends Applet
{
String text="ghq is a student!";
public void paint(Graphics g)
{
g.drawString(text,20,20);
} //在坐标20,20 处显示text的内容
}
这是最基本的Java Applet,运行的时候仅显示“ghq is a student!”。Java支持Unicode,因此中文也能在Java中很好地显示出来,我们把“ghq is a student!”改成“你好!欢迎参观!”,同样可以显示(如果无法正确显示,则是浏览器的Bug,如用的IE4.0 就存在这样的问题,请改用Netscape 或IE5.0 以上版本)。值得注意的是,在Java中每个字符用16位来表示,而不是8位,这与C语言是不同的。
[文章导读]与用户的交互是Java的主要作用,也正是Java吸引人的原因,用户可以通过鼠标和键盘与Java Applet程序对话
与用户的交互是Java的主要作用,也正是Java吸引人的原因,用户可以通过鼠标与Java Applet程序对话。我们先来看响应鼠标的例子: //Mouse.java
import java.awt.*;
import java.applet.*;
public class Mouse extends Applet
{
String text="";
public void paint(Graphics g)
{
g.drawString(text,20,20);
}
public boolean mouseDown(Event evt,int x,int y)//鼠标按下处理函数
{
text="Mouse Down";
repaint();
return true;
}
public boolean mouseUp(Event evt,int x,int y)//鼠标松开处理函数
{
text="";
repaint();
return true;
}
}
当用户点击程序时,程序将显示"Mouse Down",说明程序对鼠标作出了响应。然而要注意Java并不区分鼠标的左右键。
我们再来看对键盘响应的例子: //Keyboard.java
import java.awt.*;
import java.applet.*;
public class Keyboard extends Applet
{
String text="";
public void paint(Graphics g)
{
g.drawString(text,20,20);}
public boolean keyDown(Event evt,int x)//键盘被按下的处理函数
{
text="Key Down";
repaint();
return true;
}
public boolean keyUp(Event evt,int x)//键盘被松开的处理函数
{
text="";
repaint();
return true;
}
}
}
当键盘被按下时,程序就会显示"Key Down",键盘松开时清除文字。利用这些函数,我们就可以用鼠标和键盘函数与用户交互。
[文章导读] Java Applet常用来显示存储在GIF文件中的图像
Java Applet常用来显示存储在GIF文件中的图像。Java Applet装载GIF图像非常简单,在Applet内使用图像文件时需定义Image对象。多数Java Applet使用的是GIF或JPEG格式的图像文件。Applet使用getImage方法把图像文件和Image对象联系起来。
Graphics类的drawImage方法用来显示Image对象。为了提高图像的显示效果,许多Applet都采用双缓冲技术:首先把图像装入内存,然后再显示在屏幕上。
Applet可通过imageUpdate方法测定一幅图像已经装了多少在内存中。
装载一幅图像
Java把图像也当做Image对象处理,所以装载图像时需首先定义Image对象,格式如下所示:Image picture;
然后用getImage方法把Image对象和图像文件联系起来:picture=getImage(getCodeBase(),"ImageFileName.GIF");
getImage方法有两个参数。第一个参数是对getCodeBase方法的调用,该方法返回Applet的URL地址,如www.sun.com/Applet。第二个参数指定从URL装入的图像文件名。如果图文件位于Applet之下的某个子目录,文件名中则应包括相应的目录路径。
用getImage方法把图像装入后,Applet便可用Graphics类的drawImage方法显示图像,形式如下所示:g.drawImage(Picture,x,y,this);
该drayImage方法的参数指明了待显示的图像、图像左上角的x坐标和y坐标以及this。
第四个参数的目的是指定一个实现ImageObServer接口的对象,即定义了imageUpdate方法的对象(该方法随后讨论)。
显示图像(ShowImage.java)//源程序清单
import java.awt.*;
import java.applet.*;
public class ShowImage extends Applet
Image picure; //定义类型为Image的成员变量
public void init()
{
picture=getImage(getCodeBase(),"Image.gif"); //装载图像
}
public void paint(Graphics g)
{
g.drawImage(picture,0,0,this); //显示图像
}
}
为此,HTML文件中有关Applet的语句如下:<HTML>
<TITLE>Show Image Applet</TITLE>
<APPLET
CODE="ShowImage.class" //class文件名为ShowImage.class
WIDTH=600
HEIGHT=400>
</APPLET>
</HTML>
编译之后运行该Applet时,图像不是一气呵成的。这是因为程序不是drawImage方法返回之前把图像完整地装入并显示的。与此相反,drawImage方法创建了一个线程,该线程与Applet的原有执行线程并发执行,它一边装入一边显示,从而产生了这种不连续现象。为了提高显示效果。许多Applet都采用图像双缓冲技术,即先把图像完整地装入内存然后再显示在屏幕上,这样可使图像的显示一气呵成。
双缓冲图像
为了提高图像的显示效果应采用双缓冲技术。首先把图像装入内存,然后再显示在Applet窗口中。
使用双缓冲图像技术例子(BackgroundImage.java)//源程序清单
import java.awt.*;
import java. applet.*;
public class BackgroundImage extends Applet //继承Applet
{
Image picture;
Boolean ImageLoaded=false;
public void init()
{
picture=getImage(getCodeBase(),"Image.gif"); //装载图像
Image offScreenImage=createImage(size().width,size().height);
//用方法createImage创建Image对象
Graphics offScreenGC=offScreenImage.getGraphics(); //获取Graphics对象
offScreenGC.drawImage(picture,0,0,this); //显示非屏幕图像
}
public void paint(Graphics g)
{
if(ImageLoaded)
{
g.drawImage(picture,0,0,null); //显示图像,第四参数为null,不是this
showStatus("Done");
}
else
showStatus("Loading image");
}
public boolean imageUpdate(Image img,int infoflags,int x,int y,int w,int h)
{
if(infoflags= =ALLBITS)
{
imageLoaded=true;
repaint();
return false;
}
else
reture true;
}
}
分析该Applet的init方法可知,该方法首先定义了一个名为offScreenImage的Image对象并赋予其createImage方法的返回值,然后创建了一个名为offScreenGC的Graphics对象并赋予其图形环境——非屏幕图像将由它来产生。因为这里画的是非屏幕图像,所以Applet窗口不会有图像显示。
每当Applet调用drawImage方法时,drawImage将创建一个调用imageUpdate方法的线程。Applet可以在imageUpdate方法里测定图像已有装入内存多少。drawImage创建的线程不断调用imageUpdate方法,直到该方法返回false为止。
imageUpdate方法的第二个参数infoflags使Applet能够知道图像装入内存的情况。该参数等于ImageLoaded设置为true并调用repaint方法重画Applet窗口。该方法最终返回false,防止drawImage的执行线程再次调用imageUpdate方法。
该Applet在paint方法里的操作是由ImageLoaded变量控制的。当该变量变为true时,paint方法便调用drawImage方法显示出图像。paint方法调用drawImage方法时把null作为第四参数,这样可防止drawImage调用imageUpdate方法。因为这时图像已装入内存,所以图像在Applet窗口的显示可一气呵成。
[文章导读]使用Applet播放声音时需首先定义AudioClip对象,GetAudioClip方法能把声音赋予AudioClip对象使用Applet播放声音时需首先定义AudioClip对象,GetAudioClip方法能把声音赋予AudioClip对象,如果仅想把声音播放一遍,应调用AudioClip类的play方法,如果想循环把声音剪辑,应选用AudioClip类的loop方法。
(1) 播放声音文件
图像格式各种各样,如BMP、GIF和JPEG等。声音文件也一样,WAV和AU是最常用的两种声音文件。目前Java仅支持AU文件,但Windows环境下常用的却是WAV文件,所以最好能有一个可把WAV文件转换为AU文件的工具。
* 播放声音的AudioClip类
AudioClip类用来在Java Applet内播放声音,该类在java.Applet包中有定义。
下面演示了如何利用AudioClip类播放声音。
装入一个名为Sample.Au的声音文件并播放(SoundDemo.java)//源程序清单
import java.awt.*;
import java.applet.*
public class SoundDemo extends Applet
{
public void paint(Graphics g)
{
AudioClip audioClip=getAudioClip(getCodeBase(),”Sample.AU”);
//创建AudioClip对象并用//getAudioClip方法将其初始化。
g.drawstring("Sound Demo! ",5,15);
audioClip.loop();//使用AudioClip类的loop方法循环播放
}
}
需把如下的HTML语句放入SoundDemo.HTML文件,为运行该Applet做准备。<HTML>
<TITLE>SoundDemo Applet</TITLE>
<APPLET CODE="SoundDemo.class" WIDTH=300 HEIGHT=200>
</APPLET>
</HTML>
编译并运行该Applet,屏幕上将显示出一个Applet窗口并伴以音乐。关闭Applet时音乐终止。
[文章导读]在Java中实现动画有很多种办法,但它们实现的基本原理是一样的,即在屏幕上画出一系列的帧来造成运动的感觉
Java 不仅提供了对图形、图像的支持,还允许用户实现连续的图像播放,即动画技术。Java 动画的实现,首先用Java.awt 包中的 Graphics 类的drawImage()方法在屏幕上画出图像,然后通过定义一个线程,让该线程睡眠一段时间,然后再切换成另外一幅图像;如此循环,在屏幕上画出一系列的帧来造成运动的感觉,从而达到显示动画的目的。
为了每秒钟多次更新屏幕,必须创建一个线程来实现动画的循环,这个循环要跟踪当前帧并响应周期性的屏幕更新要求;实现线程的方法有两种,可以创建一个类Thread 的派生类,或附和在一个Runnable 的界面上。
* 动画技巧
在编写动画过程时,遇到最常见的问题是屏幕会出现闪烁现象。闪烁有两个原因:一是绘制每一帧花费的时间太长(因为重绘时要求的计算量大);二是在每次调用Pain()前,Java 会用背景颜色重画整个画面,当在进行下一帧的计算时,用户看到的是背景。
有两种方法可以明显地减弱闪烁:重载 update()或使用双缓冲。
(1) 重载 update()
当AWT接收到一个applet的重绘请求时,它就调用applet的 update(),默认地,update() 清除applet的背景,然后调用 paint()。重载 update(),将以前在paint()中的绘图代码包含在update()中,从而避免每次重绘时将整个区域清除。下面是 update()方法的原始程序代码:public void update(Graphics g)
{
//首先用背景色来绘制整个画面
g.setColor(getBackGround());
g.fillRect(0,0,width,height);
//接着设置前景色为绘制图像的颜色,然后调用paint()方法
g.setColor(getForeGround());
paint(g);
}
所以要消除画面闪烁就一定要改写 update() 方法,使该方法不会清除整个画面,只是消除必要的部分。
(2) 使用双缓冲技术
另一种减小帧之间闪烁的方法是使用双缓冲,它在许多动画Applet中被使用。其主要原理是创建一个后台图像,将需要绘制的一帧画入图像,然后调用DrawImage()将整个图像一次画到屏幕上去;好处是大部分绘制是离屏的,将离屏图像一次绘至屏幕上比直接在屏幕上绘制要有效得多,大大提高做图的性能。
双缓冲可以使动画平滑,但有一个缺点,要分配一张后台图像,如果图像相当大,这将需要很大一块内存;当你使用双缓冲技术时,应重载 update()。
下面举一个时钟的例子来说明如何处理动画//AnimatorDemo.java
import java.util.*;
import java.awt.*;
import java.applet.*;
import java.text.*;
public class AnimatorDemo extends Applet implements Runnable
{
Thread timer; // 用于显示时钟的线程
int lastxs, lastys, lastxm,
lastym, lastxh, lastyh;
SimpleDateFormat formatter; //格式化时间显示
String lastdate; // 保存当前时间的字符串
Font clockFaceFont; //设置显示时钟里面的数字的字体
Date currentDate; // 显示当前时间
Color handColor; // 用于显示时针、分针和表盘的颜色
Color numberColor; // 用于显示秒针和数字的颜色
public void init()
{
int x,y;
lastxs = lastys = lastxm = lastym = lastxh = lastyh = 0;
formatter = new SimpleDateFormat ("yyyy EEE MMM dd hh:mm:ss ");
currentDate = new Date();
lastdate = formatter.format(currentDate);
clockFaceFont = new Font("Serif", Font.PLAIN, 14);
handColor = Color.blue;
numberColor = Color.darkGray;
try {
setBackground(new Color(Integer.parseInt(getParameter("bgcolor"),16)));
} catch (Exception E) { }
try {
handColor = new Color(Integer.parseInt(getParameter("fgcolor1"),16));
} catch (Exception E) { }
try {
numberColor = new Color(Integer.parseInt(getParameter("fgcolor2"),16));
} catch (Exception E) { }
resize(300,300); // 设置时钟窗口大小
}
// 计算四分之一的圆弧
public void plotpoints(int x0, int y0, int x, int y, Graphics g)
{
g.drawLine(x0+x,y0+y,x0+x,y0+y);
g.drawLine(x0+y,y0+x,x0+y,y0+x);
g.drawLine(x0+y,y0-x,x0+y,y0-x);
g.drawLine(x0+x,y0-y,x0+x,y0-y);
g.drawLine(x0-x,y0-y,x0-x,y0-y);
g.drawLine(x0-y,y0-x,x0-y,y0-x);
g.drawLine(x0-y,y0+x,x0-y,y0+x);
g.drawLine(x0-x,y0+y,x0-x,y0+y);
}
// 用Bresenham算法来画圆,其中(x0,y0)是圆的中心,r为圆半径
public void circle(int x0, int y0, int r, Graphics g)
{
int x,y;
float d;
x=0;
y=r;
d=5/4-r;
plotpoints(x0,y0,x,y,g);
while (y>x) {
if (d<0) {
d=d+2*x+3;
x++;
}
else {
d=d+2*(x-y)+5;
x++;
y--;
}
plotpoints(x0,y0,x,y,g);
}
}
public void paint(Graphics g)
{
int xh, yh, xm, ym, xs, ys, s = 0, m = 10, h = 10, xcenter, ycenter;
String today;
currentDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("s",Locale.getDefault());
try {
s = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
s = 0;
}
formatter.applyPattern("m");
try {
m = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
m = 10;
}
formatter.applyPattern("h");
try {
h = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
h = 10;
}
formatter.applyPattern("EEE MMM dd HH:mm:ss yyyy");
today = formatter.format(currentDate);
//设置时钟的表盘的中心点为(80,55)
xcenter=80;
ycenter=55;
// a= s* pi/2 - pi/2 (to switch 0,0 from 3:00 to 12:00)
// x = r(cos a) + xcenter, y = r(sin a) + ycenter
xs = (int)(Math.cos(s * 3.14f/30 - 3.14f/2) * 45 + xcenter);
ys = (int)(Math.sin(s * 3.14f/30 - 3.14f/2) * 45 + ycenter);
xm = (int)(Math.cos(m * 3.14f/30 - 3.14f/2) * 40 + xcenter);
ym = (int)(Math.sin(m * 3.14f/30 - 3.14f/2) * 40 + ycenter);
xh = (int)(Math.cos((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + xcenter);
yh = (int)(Math.sin((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + ycenter);
//画时钟最外面的圆盘其中心在(xcenter,ycenter)半径为50
g.setFont(clockFaceFont);
g.setColor(handColor);
circle(xcenter,ycenter,50,g);
//画时钟表盘里的数字
g.setColor(numberColor);
g.drawString("9",xcenter-45,ycenter+3);
g.drawString("3",xcenter+40,ycenter+3);
g.drawString("12",xcenter-5,ycenter-37);
g.drawString("6",xcenter-3,ycenter+45);
// 如果必要的话抹去然后重画
g.setColor(getBackground());
if (xs != lastxs || ys != lastys) {
g.drawLine(xcenter, ycenter, lastxs, lastys);
g.drawString(lastdate, 5, 125);
}
if (xm != lastxm || ym != lastym) {
g.drawLine(xcenter, ycenter-1, lastxm, lastym);
g.drawLine(xcenter-1, ycenter, lastxm, lastym); }
if (xh != lastxh || yh != lastyh) {
g.drawLine(xcenter, ycenter-1, lastxh, lastyh);
g.drawLine(xcenter-1, ycenter, lastxh, lastyh); }
g.setColor(numberColor);
g.drawString("", 5, 125);
g.drawString(today, 5, 125);
g.drawLine(xcenter, ycenter, xs, ys);
g.setColor(handColor);
g.drawLine(xcenter, ycenter-1, xm, ym);
g.drawLine(xcenter-1, ycenter, xm, ym);
g.drawLine(xcenter, ycenter-1, xh, yh);
g.drawLine(xcenter-1, ycenter, xh, yh);
lastxs=xs; lastys=ys;
lastxm=xm; lastym=ym;
lastxh=xh; lastyh=yh;
lastdate = today;
currentDate=null;
}
//applet的启动方法
public void start()
{
timer = new Thread(this);
timer.start();
}
// applet的停止方法
public void stop()
{
timer = null;
}
//线程的run方法
public void run()
{
Thread me = Thread.currentThread();
while (timer == me) {
try {
Thread.currentThread().sleep(1000);
}
catch (InterruptedException e) {
}
repaint();
}
}
//注意:这里重写了update()方法,只是调用了paint()方法来消除闪烁现象
public void update(Graphics g)
{
paint(g);
}
}
下面是运行该Applet 需要的AnimatorDemo.html 的内容<HTML>
<HEAD>
<TITLE>一个时钟的例子</TITLE>
</HEAD>
<BODY>
<hr>
<applet codebase="." ALIGN=MIDDLE code="AnimatorDemo.class" width=200 height=150>
</applet>
</BODY>
</HTML>
在有些情况下,可能需要在发生某事件时伴之以声音,尢其是在Applet 中装载图像的同时播放声音,这样将大大地丰富Applet的内容。协调使用图像的声音是十分重要的。
声音和图像的协调(Appletl.java)
//源程序清单
import java.awt.*;
import java.applet.*;
import java.util.*;
public class Appletl extends Applet implements Runnable
{
AudioClip audioClip;
Thread ShapeThread=null;
Random RandomNumber=new Random( );
Color ImageColor;
public void init( )
{
audioClip=getAudioClip(getCodeBase( ), "Sample.AU");// 创建一个AudioClip对象
}
public void start( )
{
if (ShapeThread= =null)
{
ShapeThread=new Thread(this);
ShapeThread.start( );
}
}
public void run()
{
while (true)
{
switch (RandomNumber.nextlnt(5)) { //把随机数转换为0~4之间的值
case 0: ImageColor=Color.black;
break;
case 1: ImageColor=Color.blue;
break;
case 2: ImageColor=Color.cyan;
break;
case3: ImageColor=Color.magenta;
break;
case4: ImageColor=Color.orange;
break;
default: ImageColor=Color.red;
}
try
{
ShapeThread.sleep(300); //线程睡眠
}
catch(InterruptedException e)
{
//忽略异常
repaint();
}
}
public void paint(Graphics g)
{
g.setColor(ImageColor);
audioClip.play(); //播放声音
switch(RandomNumber.nextlnt(2)) //获取随机数与2整除的余数
{
case0:g.fillRect(25,25,200,200); //添充一个矩形
break;
default:g.fillOval(25,25,200,200); //添充一个椭圆
break;
}
}
}该Applet的声音处理非常简单。它首先创建一个AudioClip对象并用getAudioClip把声音文件赋予该对象,然后用AudioClip类的play方法播放声音。该Applet使用Random对象产生随机数。它首先根据随机数确定颜色;然后在pai, nt内根据随机数确定画圆还是画方。Random类的nexsInt函数返回一个随机整数(int型)。该Applet把随机数转换为一个0~4之间的值(在run函数内)和一个0~1之间的值(在paint函数内)。
需把如下的HTML语句放入Appletl.HTML文件,为运行该Appletl做准备。
<HTML>
<TITLE>Applet</TITLE>
<APPLET CODE="Appletl.class" WIDTH=300 HEIGHT=300>
</APPLET>
</HTML>编译并运行该Appletl,屏幕上将显示出一个Applet窗口,窗口中不

