將列表數據提供給 applet 看過關於 applet 參數化的上一篇技巧的讀者可能已經注意到
我們的方法沒有對一類重要的對象進行初始化
在本文中
我們將研究如果利用類反射機制對一維數組和二位數組進行初始化
我知道只有實現對更高維數組的處理才能使狂熱的科學家滿意
但我將把那項工作作為練習留給您
在我的上一篇技巧中
只能處理基本類型的數組和字符串數組
考慮到任何對象最終都能由基本數據類型和字符串構建而來
所以這將不會構成多大的限制
當然
很容易將我們的技術加以擴展
之後就能直接對其他類型的數據進行初始化
數組是用來存儲列表數據的理想數據結構
我們的技術使得向 applet 傳遞列表參數變得很簡單
通常利用動態生成 HTML 文檔的程序(如 servlet 或 CGI 腳本)將列表數據傳遞給 applet
作為示例
我們設想一個比賽記分板 applet
HTML 生成器將會將當前的記分板數據庫輸出到 PARAM 標記中
接著相應的數組將被完全初始化
這要歸功於我們的參數提取方法
列表數據項的語法 我們要實現的就是一個從 PARAM 標記中提取一維或是二維數組的方法
一維數組的語法是
PARAM NAME=
myArray
VALUE=
element
element
elementN
各元素之間的定界符是空格
二位數組的語法是
PARAM NAME=
myMatrix
VALUE=
element
element
element
|
element
element
element
|
element
element
element
各行之間的定界符是 | 符號
這裡
myMatrix 是一個 (
x
) 數組
注意
Java 支持不規則數組
不規則數組就是各行的長度不同的數組
例如
HTML 作者可能會按以下方式輸入帕斯卡三角形
PARAM NAME=
pascalTriangle
VALUE=
|
|
|
|
|
|
初始化完成之後
pascalTriangle 域的內容將是
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
pascalTriangle[
] = {
}
通常
程序員應該只聲明 pascalTriangle
而不進行內存分配
我們的提取方法負責分配內存
但讓我們假定已為第四行分配了內存
如下所示
pascalTriangle[
] = new int[
];
我們的方法將只提取前兩個元素
這樣
第四行的初始化結果將是
pascalTriangle[
] = {
}
數組知識回顧 正如您在以上代碼清單中看到的那樣
我們的方法實現有點
深奧
因此
在研究源代碼之前回顧有關數組的幾點知識是個不錯的主意
我們都對 Java 的類型層次結構比較熟悉
Java 有一組預定義的基本數據類型(int
float
)
還有 Object 的子類的一個繼承樹
Object 類是所有類的最終超類
但 Java 中還存在一個不很出名的平行層次結構
我稱其為數組層次結構
您無論何時在類型層次結構中定義了一個新類型 Foo
您實際上也同時定義了一個自動結合到數組層次結構中的新類型 Foo[]
數組層次結構中的每個類(基本數據類型的數組除外)都是 Object[] 的子類
容易引起混淆的是
Object[] 和基本數據類型的數組都是 Object 的子類
圖
表明了這一點
圖 兩個平行的層次結構 令人感到奇怪的是
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 = untTokens();
// 為數組元素賦值
//
// 請注意
我們確保索引不會超出范圍
可能未給數組分配組足夠的空間
// 以致無法容納分析後的全部元素
for (int j =
; 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(
));
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 技巧
中實現)進行了擴展
在其中添加了一個條件語句
這個條件語句高速緩存數組域並對它們進行初始化
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 =
; 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());
}
/***************
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26052.html