国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

applet的參數化--對數組進行初始化

2019-11-17 06:24:40
字體:
來源:轉載
供稿:網友

  作者: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編程技巧

下一篇:Applet和Application

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鱼台县| 溧水县| 西藏| 乌拉特后旗| 台中市| 静宁县| 信丰县| 汉源县| 乌鲁木齐市| 兴文县| 龙里县| 海宁市| 长岛县| 阳信县| 仙游县| 科尔| 扎囊县| 临泉县| 平安县| 深圳市| 辽源市| 大安市| 平阳县| 富源县| 申扎县| 盖州市| 永新县| 睢宁县| 宁河县| 榆社县| 霍林郭勒市| 贵港市| 嘉禾县| 西华县| 田林县| 沽源县| 临颍县| 佛坪县| 报价| 喀喇沁旗| 辽阳县|