动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > Java教程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ Java教程 ]的信息

本月文章推荐
.JBuilder和JDeveloper的简单比较.
.关于Java中各种修饰符与访问修饰.
.编写高级应用程序2.
.从零开始到您如何成为EJB专家.
.Resin服务器的使用 -Java Web服务.
.Apusic 应用服务器简介.
.The Alloy Look And Feel 1.4.4破.
.前进:从EJB 2.1到EJB 3.0.
.Eclipse插件的外挂安装方法.
.java 中添加一个全局快捷键.
.JFCUnit测试GUI的一个实例(代码.
.Java入门:java中几个关于类的名.
.Java高级日期概念三.
.开源技术:tomcat5.0下配置ssl.
.Struts中用动态选择的元素创建复.
.Struts源码研究-html-Link标签篇.
.OReilly新书推荐:《游戏开发物理.
.求出e=1+1/1!+1/2!+1/3!+……+1/.
.直取Sun大本营,IBM发表Linux服务.
.详解Java规则引擎与其API.

applet的参数化--对数组进行初始化

发表日期:2008-1-5 |



  作者:Yvon Sauvageau

摘要
将 applet 参数化通常是一项烦琐的工作,包括在 applet 的 init() 方法中添加很多重复的代码行。上个月,Java 技巧的撰稿人 Yvon Sauvageau 向您讲述了如何将这些代码缩减为一行,这要归功于类反射机制。但是,他没有说明如何实现数组初始化。本技巧将说明使数组自动初始化的细节。
上个月,我在 Java Tip 57,"Applet parameterization via class reflection" 中提供了一个出色的方法来使 applet 参数化。参数化是指借助嵌入在 Html 文件中的 PARAM 标记来传递参数。这个方法用起来很方便,因为它完成了全部的参数提取工作,使您在相当长的时间内不必输入 getParameter。但在上一篇技巧中我没提及如何实现数组初始化。本月的这篇技巧将为您讲述这个问题。
本文假定您比较熟悉类反射机制,尽管您可能在查看代码时就能对它有所了解。类反射机制的功能部件位于 java.lang.reflect 软件包内。这种机制使您能够检查类的结构,还可提供域、方法、访问修饰符等的运行时信息。

将列表数据提供给 applet
看过关于 applet 参数化的上一篇技巧的读者可能已经注重到:我们的方法没有对一类重要的对象进行初始化。在本文中,我们将研究假如利用类反射机制对一维数组和二位数组进行初始化。我知道只有实现对更高维数组的处理才能使狂热的科学家满足,但我将把那项工作作为练习留给您。

在我的上一篇技巧中,只能处理基本类型的数组和字符串数组。考虑到任何对象最终都能由基本数据类型和字符串构建而来,所以这将不会构成多大的限制。当然,很轻易将我们的技术加以扩展,之后就能直接对其他类型的数据进行初始化。

数组是用来存储列表数据的理想数据结构。我们的技术使得向 applet 传递列表参数变得很简单。 通常利用动态生成 HTML 文档的程序(如 servlet 或 CGI 脚本)将列表数据传递给 applet。作为示例,我们设想一个比赛记分板 applet。HTML 生成器将会将当前的记分板数据库输出到 PARAM 标记中,接着相应的数组将被完全初始化 -- 这要归功于我们的参数提取方法。

列表数据项的语法
我们要实现的就是一个从 PARAM 标记中提取一维或是二维数组的方法。一维数组的语法是:

PARAM NAME="myArray" VALUE="element1 element2 ... elementN"


各元素之间的定界符是空格。

二位数组的语法是:

PARAM NAME="myMatrix" VALUE="element11 element12 element13
element21 element22 element23
element31 element32 element33"


各行之间的定界符是 符号。这里,myMatrix 是一个 (3 x 3) 数组。

注重:Java 支持不规则数组。 不规则数组就是各行的长度不同的数组。例如,HTML 作者可能会按以下方式输入帕斯卡三角形:

PARAM NAME="pascalTriangle" VALUE=" 1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1"


初始化完成之后,pascalTriangle 域的内容将是:

pascalTriangle[0] = {1}
pascalTriangle[1] = {1, 1}
pascalTriangle[2] = {1, 2, 1}
pascalTriangle[3] = {1, 3, 3, 1}
pascalTriangle[4] = {1, 4, 6, 4, 1}
pascalTriangle[5] = {1, 5, 10, 10, 5, 1}
pascalTriangle[5] = {1, 6, 15, 20, 15, 6, 1}


通常,程序员应该只声明 pascalTriangle,而不进行内存分配。我们的提取方法负责分配内存。但让我们假定已为第四行分配了内存,如下所示:

pascalTriangle[3] = new int[2];


我们的方法将只提取前两个元素。这样,第四行的初始化结果将是:

pascalTriangle[3] = {1, 3}



数组知识回顾
正如您在以上代码清单中看到的那样,我们的方法实现有点“深奥”。因此,在研究源代码之前回顾有关数组的几点知识是个不错的主意。

我们都对 Java 的类型层次结构比较熟悉:Java 有一组预定义的基本数据类型(int、float...),还有 Object 的子类的一个继续树,Object 类是所有类的最终超类。但 Java 中还存在一个不很出名的平行层次结构,我称其为数组层次结构。您无论何时在类型层次结构中定义了一个新类型 Foo,您实际上也同时定义了一个自动结合到数组层次结构中的新类型 Foo[]。数组层次结构中的每个类(基本数据类型的数组除外)都是 Object[] 的子类。轻易引起混淆的是:Object[] 和基本数据类型的数组都是 Object 的子类。图 1 表明了这一点。


图 1:两个平行的层次结构


令人感到希奇的是,Java 根本就没有多维数组,只有一维数组。多维数组实际上是“一维数组的数组的数组的数组...”。因此,我们可以创建不规则数组。事实上,我们甚至可以不对某些行进行初始化,而将它们保留为空值。

数组提取方法的实现
现在我们可以查看源代码了。正如您所见,其中加了大量注释。通常,包含如此多的注释不是个好习惯,但在这里,我们要将已经抽象的 Java 数组包装在由类反射机制提供的元数据抽象层中。结果,多数程序语句都不能表明其自身的含义,所以在这种情况下对几乎每个代码行作注释是无可非议的。

无论何时对一维或是二维数组进行初始化,最终我们都需要用 HTML 作者输入的行对一维数组进行初始化。我们设计了一个方法来完成这一操作:

/**
* 用符号处理器 (tokenizer) 的内容填充一维数组。
* 符号被转换为数组的内容类型。
*
* @param array 要填充的数组。
* @param elementTokens 包含要填入数组的符号的符号处理器。
*/
private static void fillOneDimensionalArray(Object array,
StringTokenizer elementTokens)
throws IllegalAccessException {

if (array != null && elementTokens != null && array.getClass().isArray()) { // 双重检验。
// 数组应该容纳哪种类型的元素?
Class componentType = array.getClass().getComponentType();

int numElements = elementTokens.countTokens();

// 为数组元素赋值。
//
// 请注重,我们确保索引不会超出范围。可能未给数组分配组足够的空间,
// 以致无法容纳分析后的全部元素。
for (int j = 0; j < Array.getLength(array) && j < numElements; j++) {
// 将符号转换为数组所容纳的类型。
// 然后将其添加到数组中。
if (componentType.equals(boolean.class))
Array.setBoolean(array, j, Boolean.valueOf(elementTokens.nextToken().trim()).booleanValue());

else if (componentType.equals(byte.class))
Array.setByte(array, j, Byte.valueOf(elementTokens.nextToken().trim()).byteValue());

else if (componentType.equals(char.class))
Array.setChar(array, j, elementTokens.nextToken().charAt(0));

else if (componentType.equals(double.class))
Array.setDouble(array, j, Double.valueOf(elementTokens.nextToken().trim()).doubleValue());

else if (componentType.equals(float.class))
Array.setFloat(array, j, Float.valueOf(elementTokens.nextToken().trim()).floatValue());

else if (componentType.equals(int.class))
Array.setInt(array, j, Integer.valueOf(elementTokens.nextToken().trim()).intValue());

else if (componentType.equals(long.class))
Array.setLong(array, j, Long.valueOf(elementTokens.nextToken().trim()).longValue());

else if (componentType.equals(short.class))
Array.setShort(array, j, Short.valueOf(elementTokens.nextToken().trim()).shortValue());

else if (componentType.equals(String.class))
Array.set(array, j, elementTokens.nextToken());
}
}
}


我们使用 Class.getComponentType() 方法获取给定数组对象所容纳的元素类型。一旦我们获得这些信息,我们就知道应将行元素转换为何种类型。这是在一个循环语句中完成的。

您可能已猜到了,Array.setByte(Object obj, int i, byte datum) 用字节变量 datum 为 obj 数组的第 i 个元素赋值。这相当于 ((byte[])obj)[i] = datum。

下面开始分析实现的核心部分。我对 Util.initializeApplet(Applet, String) 方法(在“Java 技巧 57”中实现)进行了扩展,在其中添加了一个条件语句,这个条件语句高速缓存数组域并对它们进行初始化。

import java.applet.*;
import java.lang.reflect.*;
import java.util.*;

public abstract class Util {

/**
* 对 applet 的名称以给定筛选前缀开头的非 final 公共域进行初始化。
* 初始值将从 HTML PARAM 标记中读取。
* *
* @param applet 要初始化的 applet。
* @param filterPrefix 只对那些以此前缀开头的域进行初始化。
*
* 假如前缀为空值,将对所有非 final 公共域进行初始化。
*/
public static void initializeApplet(Applet applet, String filterPrefix) {

Class metaclass = applet.getClass();
Field[] fields = metaclass.getFields();
String param = null;

for (int i = 0; i < fields.length; i++) {
try {
param = applet.getParameter(fields[i].getName());

if (param == null
Modifier.isFinal(fields[i].getModifiers())
((filterPrefix != null) &&
!fields[i].getName().startsWith(filterPrefix))
)
continue;

Class fieldType = fields[i].getType();

if (fieldType.equals(boolean.class)) {
fields[i].setBoolean(applet, Boolean.valueOf(param).booleanValue());
}
else if (fieldType.equals(byte.class)) {
fields[i].setByte(applet, Byte.valueOf(param).byteValue());
}

/*********************************************

* 具体细节已被删除。请参阅上一篇文章。

* 要获得完整的代码,请下载源文件。

*********************************************/

// 对数组进行初始化。
else if (fieldType.isArray()) {

// 此处我们知道正在处理的域是一个数组。
// 但数组要容纳何种类型的元素呢?
Class componentType = fieldType.getComponentType();

// 对一维数组进行初始化。
if (componentType.isPrimitive()
componentType.equals(String.class)) {

// 用 StringTokenizer 分析由 HTML 作者提供的数组元素。

StringTokenizer elementTokens = new StringTokenizer(param);
int numElements = elementTokens.countTokens();

// 请注重,fields[i] 只是表示数组对象的元数据域。
// 我们需要一个数组引用,以便为它的元素赋值。
//
// 这样:
Object array = fields[i].get(applet);

// 如因某种原因已构造了数组,
// 则使它保持原样。

// 否则:以适当的类型构造数组,
// 并为其分配足够的内存空间,
// 以容纳由 HTML 作者提供全部元素。
if (array == null) {
// 构造类型为 componentType 的数组,
// 并为 numElements 分配空间。
fields[i].set(applet, Array.newInstance(componentType, numElements));

// 获取刚构造的数组的引用。
array = fields[i].get(applet);
}

// 用包含在 elementTokens 中的元素填充数组。
fillOneDimensionalArray(array, elementTokens);
}

// 对二维数组进行初始化。
else if (componentType.isArray() &&
(componentType.getComponentType().isPrimitive()
componentType.getComponentType().equals(String.class))) {

// 子数组(即各行)由 "" 符号分隔。
// 使用这种定界符将二维表分解成一系列表示一维数组的
// 符号。我们称之为子数组。
StringTokenizer subarrayTokens = new StringTokenizer(param, "");
int numSubarrays = subarrayTokens.countTokens();

// 请注重,"fields[i]" 只是表示数组对象的元数据域。
// 我们需要此数组的一个引用,以便为它的元素赋值。
//
// 这样:
Object array = fields[i].get(applet);

// 如因某种原因已构造了数组,
// 则使它保持原样。
//
// 否则:以适当的类型构造数组,
// 并为其分配足够的内存空间,
// 以容纳 HTML 作者提供的全部行。
//
// 请注重,此处的变量 "array" 必定是数组的数组。
// 因此“适当的类型”必定是数组类型。
if (array == null) {
// 构造类型为 componentType 的数组,
// 并为 numSubarrays 行分配内存空间。
fields[i].set(applet, Array.newInstance(componentType, numSubarrays));

// 获取刚构造的数组的数组的引用。
array = fields[i].get(applet);
}

// 依次对每个子数组进行初始化。

// 请注重,我们确保索引不超出范围。
// 可能是为数组分配的空间不足,
// 以致无法容纳 HTML 作者提供的所有行。
for (int j = 0; j < Array.getLength(array) && j < numSubarrays; j++) {
// 用 StringTokenizer 分析由 HTML 作者提供的行元素。
StringTokenizer elementTokens = new StringTokenizer(subarrayTokens.nextToken());
int numElements = elementTokens.countTokens();

// 引入此新变量的唯一目的就是使代码更易于阅读。
// 但稍后我们就会看到这引起了敏感的争论。 Object subArray = ((Object[])array)[j];

// 如因某种原因已构造了子数组,
// 则使它保持原样。
//
// 否则:以适当的类型构造子数组,并为其分配充足的内存空间,
// 以便容纳由 HTML 作者提供的全部元素。

if (subArray == null) {
// "componentType" 是 "array" 的类型,因此子数组的类型 "componentType.getComponentType()" 的数组即为 "subArray" 的类型。

subArray = Array.newInstance(componentType.getComponentType(), numElements);
}

// 下面的语句是必要的。在前面的条件语句中,
// 可能已为 "subarray" 分配了一个不同于
// "((Object[]array)[j]" 的对象引用。
// 这样就为这两个变量重新分配的别名。
// 这样就确保应用于子数组的全部变化
// 将在子数组的数组(即 "array")中得到反映。


((Object[])array)[j] = subArray;

// 用 elementTokens 中包含的元素填充子数组。
fillOneDimensionalArray(subArray, elementTokens);
}
}
}
}

catch (Exception e) {
System.err.println(e + " while initializing " + fields[i]);
}
}
}
}


当 type 为 {void.class, boolean.class, byte.class, short.class, int.class, long.class, float.class, double.class} 之一时,type.isPrimitive() 方法即返回 "true"。

分配数组的类反射方法如下所示:

bazField.set(fooObject, Array.newInstance(componentType, numElements));


这将创建数组并为其分配存储空间,然后用该数组为由变量 bazField 表示且属于变量 fooObject 的域赋值。换句话说,fooObject 类的 FooClass 有一个由 bazField 表示的属性,同时我们用这个数组为 FooClass 的 fooObject 实例中的相应域赋值。

以下语句的语法和语义都可能令您吃惊。

Object subArray = ((Object[])array)[j];


该语句的目的是从名为 array 的二维数组中提取第 j 行。我们将该行存储在变量 subArray 中。这个变量的类型 Object,因为我们预先不知道要处理的一维数组为何种类型;回忆一下前面关于数组的讨论,任何类型的一维数组的最近公共超类即 Object 类。若不是另有原因,该变量数组的类型也是 Object。我们预先不知道要处理的是基本类型的一维数组,还是其他类型的数组,同样,最近的公共超类仍是 Object。当我们看到这条语句时,我们已经知道了这样一个事实:变量 array 表示一个二维数组;回忆一下我们关于数组的讨论,二维数组实际上是一维数组的一维数组。因此,它们实际上都是 Object[] 的子类。这就是我们能将变量 array 转换为 Object[] 的原因所在。接下来的工作就很简单了,提取第 j 行作为 Object 实例,并将其赋给变量 subArray。

小结
本技巧说明了如何利用类反射机制来减轻程序员开发可配置 applet 的负担。本月的这篇技巧用数组提取例程对我们的技术作了补充。通过使用一个完成所有参数提取工作的方法,您就免除了不得不一再输入 getParameter 的乏味工作。


作者简介
yvon.sauvageau Yvon 获得了 McGill 大学(位于加拿大蒙特利尔市)的数学及计算机科学学位。他有七年的编程经验,范围涉及商业服务器应用程序到 GUI 编程。两年前他迷上了 Java(没办法!)。他去年一月份通过了 Sun 认证 Java 程序员考试,最近又通过了 Sun 认证 Java 开发员考试。他目前是巴黎 MTLI-NSK 技术公司的一名咨询人员。他目前承担的项目是一个人力资源治理系统,该系统完全用 Java 编写,并且基于 ObjectStore OODBMS。在欧洲的生活很有乐趣,但作为加拿大人他很想念曲棍球!

参考资源

下面是本技巧的源代码(zip 格式):
javatip59.zip
下面是本技巧的源代码(jar 格式):
javatip59.jar
请参阅我的上一篇技巧,"Java Tip 57: Applet parameterization via class reflection"
http://www.javaworld.com/javatips/jw-javatip57.html
有关类反射的信息,请参阅 ChUCk McManis 在 JavaWorld 发表的文章,"Take an in-depth look at the Java Reflection API"
http://www.javaworld.com/javaworld/jw-09-1997/jw-09-indepth.html
上一篇:Applet和Application 人气:821
下一篇:Applet编程技巧 人气:551
浏览全部Java的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-9-4 LPLY CMS 网站管理系统 v5.0
2008-9-4 缤纷互动视频交友 v3.01.902
2008-9-4 ADN视频收藏专家 v3.0 bulid 080
2008-9-4 天空网络电影系统SKYUC v2.5.6 简
2008-9-4 Web Wiz Rich Text Editor(文本编
2008-9-4 幻影动漫网视频系统(Ppdong) v1.
2008-9-4 乐维电脑在线DIY配置系统
2008-9-4 老樊文章管理系统SQL版
2008-9-4 ASP.NET 2.53 缩略图水印组件源码
2008-8-23 Mini WinMount V0.4
2008-8-23 Vista优化大师3.11正式版
2008-8-23 Wine 1.13
2008-8-23 KlipFolio 5.0 Build 5899-80
2008-8-23 Windows Sysinternals Desktops
2008-8-23 OneTap Movies1.2破解版
2008-8-23 AnnotaterPDF阅读1.1.503 破解版
2008-8-23 SoundMeter分贝测量仪 v1.0汉化破
2008-8-23 iDrum音乐节拍1.0破解版
  发表评论
姓 名: 验证码:
内 容:
站长工具:网站收录查询 | Google PR查询 | ALEXA排名查询 | CSS在线编辑器 | 广告代码 | Html转换js | js/vbs加密 | md5加密 | 进制转换
实用工具:汉字翻译拼音 | 符号对照表 | 个税计算 | 经典小工具 | 汉字简繁转换 | 普通单位换算 | 公制单位换算 | 生辰老黄历 | 国内电话区号 国家代码与域名缩写 | 文字加密解密 | 健康查询 | 万年历 | 汉字横竖排版 | 手机号码查询 | 计算器 | ip搜索
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号
ホームページ制作 不動産検索システム 求人情報
防水工事·改修工事 フットサル大会 探偵