# 13.2 基本程序片
库通常按照它们的功能来进行组合。一些库,例如使用过的,便中断搁置起来。标准的Java库字符串和向量类就是这样的一个例子。其他的库被特殊地设计,例如构建块去建立其它的库。库中的某些类是应用程序的框架,其目的是协助我们构建应用程序,在提供类或类集的情况下产生每个特定应用程序的基本活动状况。然后,为我们定制活动状况,必须继承应用程序类并且废弃程序的权益。应用程序框架的默认控制结构将在特定的时间调用我们废弃的程序。应用程序的框架是“分离、改变和中止事件”的好例子,因为它总是努力去尝试集中在被废弃的所有特殊程序段。
程序片利用应用程序框架来建立。我们从类中继承程序片,并且废弃特定的程序。大多数时间我们必须考虑一些不得不运行的使程序片在WEB页面上建立和使用的重要方法。这些方法是:
```
Method
Operation
init( )
Called when the applet is first created to perform first-time initialization of the applet
start( )
Called every time the applet moves into sight on the Web browser to allow the applet to start up its normal operations (especially those that are shut off by stop( )). Also called after init( ).
paint( )
Part of the base class Component (three levels of inheritance up). Called as part of an update( ) to perform special painting on the canvas of an applet.
stop( )
Called every time the applet moves out of sight on the Web browser to allow the applet to shut off expensive operations. Also called right before destroy( ).
destroy( )
Called when the applet is being unloaded from the page to perform final release of resources when the applet is no longer used
```
| 方法 | 作用 |
| --- | --- |
| `init()` | 程序片第一次被创建,初次运行初始化程序片时调用 |
| `start()` | 每当程序片进入Web浏览器中,并且允许程序片启动它的常规操作时调用(特殊的程序片被`stop()`关闭);同样在`init()`后调用 |
| `paint()` | 基类`Component`的一部分(继承结构中上溯三级)。作为`update()`的一部分调用,以便对程序片的画布进行特殊的描绘 |
| `stop()` | 每次程序片从Web浏览器的视线中离开时调用,使程序片能关闭代价高昂的操作;同样在调用`destroy()`前调用 |
| `destroy()` | 程序片不再需要,将它从页面中卸载时调用,以执行资源的最后清除工作 |
现在来看一看`paint()`方法。一旦`Component`(目前是程序片)决定自己需要更新,就会调用这个方法——可能是由于它再次回转屏幕,首次在屏幕上显示,或者是由于其他窗口临时覆盖了你的Web浏览器。此时程序片会调用它的`update()`方法(在基类`Component中`定义),该方法会恢复一切该恢复的东西,而调用`paint()`正是这个过程的一部分。没必要对`paint()`进行重载处理,但构建一个简单的程序片无疑是方便的方法,所以我们首先从`paint()`方法开始。
`update()`调用`paint()`时,会向其传递指向`Graphics`对象的一个引用,那个对象代表准备在上面描绘(作图)的表面。这是非常重要的,因为我们受到项目组件的外观的限制,因此不能画到区域外,这可是一件好事,否则我们就会画到线外去。在程序片的例子中,程序片的外观就是这界定的区域。
图形对象同样有一系列我们可对其进行的操作。这些操作都与在画布上作图有关。所以其中的大部分都要涉及图像、几何菜状、圆弧等等的描绘(注意如果有兴趣,可在Java文档中找到更详细的说明)。有些方法允许我们画出字符,而其中最常用的就是`drawString()`。对于它,需指出自己想描绘的`String`(字符串),并指定它在程序片作图区域的起点。这个位置用像素表示,所以它在不同的机器上看起来是不同的,但至少是可以移植的。
根据这些信息即可创建一个简单的程序片:
```
//: Applet1.java
// Very simple applet
package c13;
import java.awt.*;
import java.applet.*;
public class Applet1 extends Applet {
public void paint(Graphics g) {
g.drawString("First applet", 10, 10);
}
} ///:~
```
注意这个程序片不需要有一个`main()`。所有内容都封装到应用程序框架中;我们将所有启动代码都放在`init()`里。
必须将这个程序放到一个Web页中才能运行,而只能在支持Java的Web浏览器中才能看到此页。为了将一个程序片置入Web页,需要在那个Web页的代码中设置一个特殊的标记(注释①),以指示网页装载和运行程序片。这就是`applet`标记,它在`Applet1`中的样子如下:
```
<applet
code=Applet1
width=200
height=200>
</applet>
```
①:本书假定读者已掌握了HTML的基本知识。这些知识不难学习,有许多书籍和网上资源都可以提供帮助。
其中,`code`值指定了`.class`文件的名字,程序片就驻留在那个文件中。width和height指定这个程序片的初始尺寸(如前所述,以像素为单位)。还可将另一些东西放入`applet`标记:用于在因特网上寻找其他`.class`文件的位置(`codebase`)、对齐和排列信息(`align`)、使程序片相互间能够通信的一个特殊标识符(`name`)以及用于提供程序片能接收的信息的参数。参数采取下述形式:
```
<Paramname=标识符 value ="信息">
```
可根据需要设置任意多个这样的参数。
在简单的程序片中,我们要做的唯一事情是按上述形式在Web页中设置一个程序片标记(`applet`),令其装载和运行程序片。
## 13.2.1 程序片的测试
我们可在不必建立网络连接的前提下进行一次简单的测试,方法是启动我们的Web浏览器,然后打开包含了程序片标签的HTML文件(Sun公司的JDK同样包括一个称为“程序片观察器”的工具,它能挑出html文件的`<applet>`标记,并运行这个程序片,不必显示周围的HTML文本——注释②)。html文件载入后,浏览器会发现程序片的标签,并查找由`code`值指定的`.class`文件。当然,它会先在`CLASSPATH`(类路径)中寻找,如果在`CLASSPATH`下找不到类文件,就在WEB浏览器状态栏给出一个错误信息,告知不能找到`.class`文件。
②;由于程序片观察器会忽略除`APPLET`标记之外的任何东西,所以可将那些标记作为注释置入Java源码:
```
// <applet code=MyApplet.class width=200 height=100></applet>
```
这样就可直接执行`appletviewer MyApplet.java`,不必再创建小的HTML文件来完成测试。
若想在Web站点上试验,还会碰到另一些麻烦。首先,我们必须有一个Web站点,这对大多数人来说都意味着位于远程地点的一家服务提供商(ISP)。然后必须通过某种途径将HTML文件和`.class`文件从自己的站点移至ISP机器上正确的目录(WWW目录)。这一般是通过采用“文件传输协议”(FTP)的程序来做成的,网上可找到许多这样的免费程序。所以我们要做的全部事情似乎就是用FTP协议将文件移至ISP的机器,然后用自己的浏览器连接网站和HTML文件;假如程序片正确装载和执行,就表明大功告成。但真是这样吗?
但这儿我们可能会受到愚弄。假如Web浏览器在服务器上找不到`.class`文件,就会在你的本地机器上搜寻`CLASSPATH`。所以程序片或许根本不能从服务器上正确地装载,但在你看来却是一切正常的,因为浏览器在你的机器上找到了它需要的东西。但在其他人访问时,他们的浏览器就无法找到那些类文件。所以在测试时,必须确定已从自己的机器删除了相关的`.class`文件,以确保测试结果的真实。
我自己就遇到过这样的一个问题。当时是将程序片置入一个`package`(包)中。上载了HTML文件和程序片后,由于包名的问题,程序片的服务器路径似乎陷入了混乱。但是,我的浏览器在本地类路径(`CLASSPATH`)中找到了它。这样一来,我就成了能够成功装载程序片的唯一一个人。后来我花了一些时间才发现原来是`package`语句有误。一般地,应该将`package`语句置于程序片的外部。
## 13.2.2 一个更图形化的例子
这个程序不会太令人紧张,所以让我们试着增加一些有趣的图形组件。
```
//: Applet2.java
// Easy graphics
import java.awt.*;
import java.applet.*;
public class Applet2 extends Applet {
public void paint(Graphics g) {
g.drawString("Second applet", 10, 15);
g.draw3DRect(0, 0, 100, 20, true);
}
} ///:~
```
这个程序用一个方框将字符串包围起来。当然,所有数字都是“硬编码”的(指数字固定于程序内部),并以像素为基础。所以在一些机器上,框会正好将字符串围住;而在另一些机器上,也许根本看不见这个框,因为不同机器安装的字体也会有所区别。
对`Graphic`类而言,可在帮助文档中找到另一些有趣的内容。大多数涉及图形的活动都是很有趣的,所有我将更多的试验留给读者自己去进行。
## 13.2.3 框架方法的演示
观看框架方法的实际运作是相当有趣的(这个例子只使用`init()`,`start()`和`stop()`,因为`paint()`和`destroy()`非常简单,很容易就能掌握)。下面的程序片将跟踪这些方法调用的次数,并用`paint()`将其显示出来:
```
//: Applet3.java
// Shows init(), start() and stop() activities
import java.awt.*;
import java.applet.*;
public class Applet3 extends Applet {
String s;
int inits = 0;
int starts = 0;
int stops = 0;
public void init() { inits++; }
public void start() { starts++; }
public void stop() { stops++; }
public void paint(Graphics g) {
s = "inits: " + inits +
", starts: " + starts +
", stops: " + stops;
g.drawString(s, 10, 10);
}
} ///:~
```
正常情况下,当我们重载一个方法时,需检查自己是否需要调用方法的基类版本,这是十分重要的。例如,使用`init()`时可能需要调用`super.init()`。然而,`Applet`文档特别指出`init()`、`start()`和`stop()`在`Applet`中没有用处,所以这里不需要调用它们。
试验这个程序片时,会发现假如最小化WEB浏览器,或者用另一个窗口将其覆盖,那么就不能再调用`stop()`和`start()`(这一行为会随着不同的实现方案变化;可考虑将Web浏览器的行为同程序片观察器的行为对照一下)。调用唯一发生的场合是在我们转移到一个不同的Web页,然后返回包含了程序片的那个页时。
- Java 编程思想
- 写在前面的话
- 引言
- 第1章 对象入门
- 1.1 抽象的进步
- 1.2 对象的接口
- 1.3 实现方案的隐藏
- 1.4 方案的重复使用
- 1.5 继承:重新使用接口
- 1.6 多态对象的互换使用
- 1.7 对象的创建和存在时间
- 1.8 异常控制:解决错误
- 1.9 多线程
- 1.10 永久性
- 1.11 Java和因特网
- 1.12 分析和设计
- 1.13 Java还是C++
- 第2章 一切都是对象
- 2.1 用引用操纵对象
- 2.2 所有对象都必须创建
- 2.3 绝对不要清除对象
- 2.4 新建数据类型:类
- 2.5 方法、参数和返回值
- 2.6 构建Java程序
- 2.7 我们的第一个Java程序
- 2.8 注释和嵌入文档
- 2.9 编码样式
- 2.10 总结
- 2.11 练习
- 第3章 控制程序流程
- 3.1 使用Java运算符
- 3.2 执行控制
- 3.3 总结
- 3.4 练习
- 第4章 初始化和清除
- 4.1 用构造器自动初始化
- 4.2 方法重载
- 4.3 清除:收尾和垃圾收集
- 4.4 成员初始化
- 4.5 数组初始化
- 4.6 总结
- 4.7 练习
- 第5章 隐藏实现过程
- 5.1 包:库单元
- 5.2 Java访问指示符
- 5.3 接口与实现
- 5.4 类访问
- 5.5 总结
- 5.6 练习
- 第6章 类复用
- 6.1 组合的语法
- 6.2 继承的语法
- 6.3 组合与继承的结合
- 6.4 到底选择组合还是继承
- 6.5 protected
- 6.6 累积开发
- 6.7 向上转换
- 6.8 final关键字
- 6.9 初始化和类装载
- 6.10 总结
- 6.11 练习
- 第7章 多态性
- 7.1 向上转换
- 7.2 深入理解
- 7.3 覆盖与重载
- 7.4 抽象类和方法
- 7.5 接口
- 7.6 内部类
- 7.7 构造器和多态性
- 7.8 通过继承进行设计
- 7.9 总结
- 7.10 练习
- 第8章 对象的容纳
- 8.1 数组
- 8.2 集合
- 8.3 枚举器(迭代器)
- 8.4 集合的类型
- 8.5 排序
- 8.6 通用集合库
- 8.7 新集合
- 8.8 总结
- 8.9 练习
- 第9章 异常差错控制
- 9.1 基本异常
- 9.2 异常的捕获
- 9.3 标准Java异常
- 9.4 创建自己的异常
- 9.5 异常的限制
- 9.6 用finally清除
- 9.7 构造器
- 9.8 异常匹配
- 9.9 总结
- 9.10 练习
- 第10章 Java IO系统
- 10.1 输入和输出
- 10.2 增添属性和有用的接口
- 10.3 本身的缺陷:RandomAccessFile
- 10.4 File类
- 10.5 IO流的典型应用
- 10.6 StreamTokenizer
- 10.7 Java 1.1的IO流
- 10.8 压缩
- 10.9 对象序列化
- 10.10 总结
- 10.11 练习
- 第11章 运行期类型识别
- 11.1 对RTTI的需要
- 11.2 RTTI语法
- 11.3 反射:运行期类信息
- 11.4 总结
- 11.5 练习
- 第12章 传递和返回对象
- 12.1 传递引用
- 12.2 制作本地副本
- 12.3 克隆的控制
- 12.4 只读类
- 12.5 总结
- 12.6 练习
- 第13章 创建窗口和程序片
- 13.1 为何要用AWT?
- 13.2 基本程序片
- 13.3 制作按钮
- 13.4 捕获事件
- 13.5 文本字段
- 13.6 文本区域
- 13.7 标签
- 13.8 复选框
- 13.9 单选钮
- 13.10 下拉列表
- 13.11 列表框
- 13.12 布局的控制
- 13.13 action的替代品
- 13.14 程序片的局限
- 13.15 视窗化应用
- 13.16 新型AWT
- 13.17 Java 1.1用户接口API
- 13.18 可视编程和Beans
- 13.19 Swing入门
- 13.20 总结
- 13.21 练习
- 第14章 多线程
- 14.1 反应灵敏的用户界面
- 14.2 共享有限的资源
- 14.3 堵塞
- 14.4 优先级
- 14.5 回顾runnable
- 14.6 总结
- 14.7 练习
- 第15章 网络编程
- 15.1 机器的标识
- 15.2 套接字
- 15.3 服务多个客户
- 15.4 数据报
- 15.5 一个Web应用
- 15.6 Java与CGI的沟通
- 15.7 用JDBC连接数据库
- 15.8 远程方法
- 15.9 总结
- 15.10 练习
- 第16章 设计模式
- 16.1 模式的概念
- 16.2 观察器模式
- 16.3 模拟垃圾回收站
- 16.4 改进设计
- 16.5 抽象的应用
- 16.6 多重分发
- 16.7 访问器模式
- 16.8 RTTI真的有害吗
- 16.9 总结
- 16.10 练习
- 第17章 项目
- 17.1 文字处理
- 17.2 方法查找工具
- 17.3 复杂性理论
- 17.4 总结
- 17.5 练习
- 附录A 使用非JAVA代码
- 附录B 对比C++和Java
- 附录C Java编程规则
- 附录D 性能
- 附录E 关于垃圾收集的一些话
- 附录F 推荐读物