blog.Ring.idv.tw

2007 May

什麼是RIA?

RIA(Rich Internet Application)近來隨著Silverlight、JavaFX和Apollo大肆地想爭奪這一塊大餅,那什麼是RIA呢?

根據Wiki - Rich Internet application的說明,RIA這個名詞是在2002年三月首先由Macromedia的一份白皮書「Macromedia Flash MX—A next-generation rich client」所提出來的,裡頭說明了RIA將俱備以下特點:

1.Provide an efficient, high-performance runtime for executing code, content and communications

2.Integrate content, communications and application interfaces into a common environment

3.Provide a powerful and extensible object model for interactivity

4.Enable rapid application development through components and re-use

5.Enable the use of web services and data services provided by application servers

6.Embrace connected and disconnected clients

7.Enable easy deployment on multiple platforms and devices

這句話「Rich Internet applications (RIA) are Web applications that have the features and functionality of traditional desktop applications.」其實就簡單扼要地解釋了RIA~

2007-05-23 13:18:52 | Add Comment

徹底剖析JDK 5.0 Varargs

本文已刊於「#140  Run!PC 旗標資訊月刊」- 2005.09

自從1995年Java語言誕生到目前為止,已陪伴無數的程式設計人員走過這十年的光陰,尤其憑藉著Write Once, Run Anywhere的口號,便吸引了許多程式開發人員所注目的焦點,當然在這演變的過程之中,Java的版本也進步到了5.0(Tiger),但是不管是大改版或是小改版,要執行Java程式仍然是透過Java虛擬機器(JVM)(註一)去解析Java 位元碼(bytecode)來運作著,進而達成跨平台的實現,此種機制和Macromedia Flash有著異曲同工之處,為何筆者會如此認為呢?其實重點就在於Flash Player就相當於Java執行環境(JRE),而Flash SWF的檔案格式就好比如Java bytecode,兩者均依賴著中介語言來達成跨平台的機制,這也就是本文所要深入探討的重點之一。

本文會透過Java bytecode來剖析Varargs底層真實的面目,來了解這些底層的實作方式,有助於觀念的釐清,對於不管是在開發程式所在乎的效能決策上,或是想考取Java國際認證的朋友們,筆者相信本文都會提供一定的幫助!

何謂Varargs?

在我們開始探討Varargs功能之前,我們先來看一個JDK 5.0 所新增的功能之一 printf,如圖一所示,我們從API Document不難發現到printf方法(method)其實就已經運用Varargs這個新功能了!

圖一. 窺示printf API

Varargs(Variable-Length Argument Lists),從字面上顧名思義我們不難猜到Varargs代表著"變動長度參數列",筆者在這裡可以先向各位讀者透露,其實Varargs底層的處理方式就是陣列,我們稍待會一一來驗證!

開始剖析Varargs

在這裡我們先來看看一個取得字串陣列長度的方法(請參考程式一),可透過反組譯器(註二)來查看底層的bytecode。(請參考圖二)

程式一

public int getStringArrayLength(String arg[])
{
	return arg.length;
}

圖二. getStringArrayLength方法的位元碼

剖析字串陣列的位元碼

從圖二的位元碼裡,我們可以明確地看到public int getStringArrayLength(java.lang.String[]);所定義傳入的參數是「一」個字串陣列,但是為何透過反組譯器所顯示出來的參數長度設定會等於2呢?(Args_size=2)這是因為我們的getStringArrayLength()方法是一個實體方法(instance method),所以第一個參數會以隱含(implicit)的方式傳入「this」,所以第一個參數就代表「this」,第二個參數才是代表字串陣列。

getStringArrayLength 方法的位元碼(bytecode):

0# aload_1 :: 將從區域變數(local variables)的索引位置「1」來載入物件參考(object reference)至Operand Stack。

說明:「在此就是用來取得參考到字串陣列的物件參考,來將它載入至Operand Stack」。

1# arraylength :: 取得陣列的長度。

說明:「會將參考到字串陣列的物件參考(object reference)從Operand Stack取出,並推入一個int整數至Operand Stack,此整數也就代表著陣列的長度」

2# ireturn :: 傳回int值。

說明:「也就是回傳在Operand Stack裡的陣列長度」

剖析Varargs的位元碼

看透了此方法的bytecode之後,我們再來看看Varargs的程式寫法(請參考程式二),然後再一次透過反組譯器來查閱底層的bytecode,我們赫然發現,果然使用Varargs的底層bytecode和使用陣列當參數的底層bytecode居然都是如出一徹地!(如圖二)從這裡我們就已經印證了,Varargs底層的實作方式就是陣列,但是它並不僅僅只是個單純的陣列取代!

程式二

public int getStringArrayLength(String... arg)
{
	return arg.length;
}

Varargs的特性與限制

Varargs仍然擁有屬於它自己的特性與限制:

Varargs允許傳遞零或一個以上相同型態的參數。(請參考程式三)

程式三

public static int getLength(String... arg)
{
	return arg.length;
}
public static void main(String[] arg)
{
	System.out.println("getLength() ==>"+getLength());
	System.out.println("getLength() ==>"+getLength("one","two","thress"));
}

使用Varargs必須放置在該方法的最後一個參數。(請參考程式四)

程式四

public int getLength(String... arg) //#正確
{
	return arg.length;
}
public int getLength(Integer count,String... arg) //#正確
{
	return arg.length;
}
public int getLength(String... arg,Integer count) //#錯誤
{
	return arg.length;
}

一個方法裡最多只能定義一個Varargs。(請參考程式五)

程式五

public int getLength(String... arg) //#正確
{
	return arg.length;
}
public int getLength(String... arg,Integer... count) //#錯誤
{
	return arg.length;
}

既然已知Varargs就是陣列

既然已經了解Varargs的底層就是陣列,所以我們也可以取而代之地將Java程式進入點改成Varargs的形式,而且也仍然可以使用陣列唯一的屬性變數「length」及陣列元素的存取!(請參考程式六)

程式六

public static void main(String... arg)
{
	System.out.println("參數長度:"+arg.length);
	System.out.println("索引值[0] => "+arg[0]);
}

在這裡有一點仍然必須注意一下,在方法多載(Overloading)的使用上,Java允許相同的方法名稱但各自擁有不同的參數列!說到這裡或許有一些讀者已經了解筆者的用意,沒錯!當我們使用陣列及Varargs在相同的方法名稱當參數列會發生什麼情形呢?(請參考程式七)

程式七

public int getLength(String... arg)
{
	return arg.length;
}
public int getLength(String[] arg)
{
	return arg.length;
}

由於Varargs底層也是陣列的緣故,所以這時候就取決於看那一個方法宣告在最前面,以上述的程式碼為例,Java編譯器會告知我們getLength(java.lang.String...) is already defined in Test!

為何需要Varargs的存在?

現在我們換個角度來思考,那為何需要Varargs的存在呢?或許某些讀者會認為底層既然是陣列,那直接使用陣列不就好了!理論上來說是的確可以這麼做,但是既然在JDK 5.0加上Varargs這個功能,必然有它的優勢與存在的必要性,我們來看底下的程式八即可明確地了解Varargs所帶來的好處:

程式八

public static void NonVarargs(String[] str)
{
	for(String s : str)
		System.out.print(s + " ");
	System.out.println();
}
public static void Varargs(String... str)
{
	for(String s : str)
		System.out.print(s + " ");	
	System.out.println();
}
public static void main(String[] arg)
{
	System.out.print("呼叫NonVarargs method:");
	NonVarargs(new String[]{"one","two","three"}); //#1

	System.out.print("呼叫Varargs method:");
	Varargs("one","two","three"); //#2
}

從上列的程式可以看出#2的程式明顯地簡化與簡單,但實際上使用Varargs仍然是透過#1的方式,

也就是說,上列的程式使用Varargs的方式時,便不需要再宣告一個字串陣列,而是透過Varargs去委託Java編譯器去做這層的轉換處理,所以我們只要將原始檔編譯成類別(Class)檔,剩下的就交給Java編譯器自動地去幫我們處理,從這裡就可以看得出來,JDK 5.0 提供了簡化程式設計人員更方便使用的語法,這也就是中介語言所帶來的極大好處之一!

剖析「傳遞零個參數至Varargs」

由於在本文前面有談到Varargs的特性之一「Varargs允許傳遞零或一個以上相同型態的參數」,在此我們直接來測試看看呼叫Varargs()方法會產生什麼樣的變化,請將上述的程式「Varargs("one","two","three"); //#2」 改成「Varargs();」即可,接著利用反組譯器來觀察Java bytecode,在觀察之後我們又可以發現到一項JDK 5.0的改革(如圖三)。

圖三. Varargs方法的部份位元碼

從bytecode之中可以明確地看到,在JDK 5.0之前的Java Compiler會自動地將需要用到串接字串的功能時,它會在底層運用StringBuffer來處理,但是在JDK 5.0之後都將改成StringBuilder來處理串接字串的功能,而由於StringBuilder 不是一個 thread safe 類別,所以在多執行緒的環境下必須注意到JDK 5.0的這項變動。

緊接著筆者就來剖析傳遞零個參數至Varargs時,究竟底層Java Compiler又是處理的呢?(如圖四)

圖四. main 方法的部份位元碼

38# iconst_0 :: 推入一個int整數至Operand Stack。

說明:「代表一個0整數推入到Operand Stack」

39# anewarray :: 建立一個參考型態的新陣列,並從Operand Stack取出一個整數當做陣列的長度,然後將參考到此陣列的arrayref再推入Operand Stack。

說明:「在此會建立一個長度為零的字串陣列」

42# invokestatic :: 呼叫類別(static)方法。

說明:「呼叫Varargs方法,並將字串陣列傳遞進去」

相信許多的讀者閱覽到此都已經豁然開朗了,從這裡我們可以完全地了解底層是如何處理「Varargs允許傳遞零或一個以上相同型態的參數」,簡而言之,上述的程式碼經由Java Compiler編譯之後,會將「Varargs();」看待成「Varargs(new String[0]);」來處理,這就是神奇的地方!^_^

#註一 = Java虛擬機器也可稱為堆疊機器(Stack Machine),表示著JVM底層的實作概念其實就是堆疊。

#註二 = JDK有提供一個javap反組譯器,可用來顯示位元碼(bytecode)。

#參考資料

1.The JavaTM Virtual Machine Specification Second Edition

2. Java 5.0 Tiger: A Developer's Notebook

本文若有任何謬誤,希望請不吝地賜教,若能指正不勝感激。

2007-05-22 21:26:31 | Comments (2)

事件處理-「onClipEvent」vs.「MovieClip.onXXX」變數範圍

「onClipEvent」and「MovieClip.onXXX」變數範圍

我們建立一個MovieClip在Stage上,並將它命名為「a_mc」,並在此MovieClip上加些程式以便驗證。

onClipEvent(load)
{
	var bar = 10;
}

在MainTimeline[1]寫入下述程式:

a_mc.onLoad = function()
{
	var foo = 20;
}

在MainTimeline[2]寫入下述程式:

trace("a_mc(bar):"+a_mc.bar);
trace("a_mc(foo):"+a_mc.foo);
stop();

結果:

a_mc(bar):10
a_mc(foo):undefined

結果卻不如預期(bar=10,foo=20),這是因為任何宣告在「function」中的變數都將被視為「區域變數(local variables)」,而由於「onClipEvent」或「on」事件都必須附著在實體的「MovieClip」之上,所以無論哪一種事件,只要宣告在「MovieClip」的變數都將為「實體變數(instance variables)」

值得一提的是「MovieClip.onXXX」的作法,其實就像是C語言中的「函式指標(function pointer)」,可以用來指向一個「function」,這也是寫Linux Kernel的相關程式時,所必要的技能。

範例下載

2007-05-22 18:23:18 | Add Comment

事件處理-「onClipEvent」vs.「MovieClip.onXXX」執行優先權

在ActionScript 3.0之前的Event Model,簡單來說就像是個各自為政的Model,若是硬要評比的話~好處是簡單易用、彈性大~而壞處是沒有統一的Event Model,容易造成學習者混淆~尤其是當一些小細節被忽略時,往往深埋著不定時炸彈,所以在事件的處理上,了解並掌握它是不可或缺的~

「onClipEvent」and「MovieClip.onXXX」執行的優先權

我們建立一個MovieClip在Stage上,並將它命名為「a_mc」,並在此MovieClip上加些程式以便驗證。

onClipEvent(load)
{
	trace("a_mc(load):"+this);
}

然而在MainTimeline也寫入下述程式:

a_mc.onLoad = function()
{
	trace("a_mc(onLoad):"+this);
}
trace("frame[1]");

然後在此MovieClip的Frame[1]也加上:

trace("a_mc(frame[1]):"+this);

最後一樣「Ctrl + Enter」來看結果:

frame[1]
a_mc(load):_level0.a_mc
a_mc(onLoad):_level0.a_mc
a_mc(frame[1]):_level0.a_mc

從結果來看就可以證明「onClipEvent」的執行優先權比「MovieClip.onXXX」來的高!

範例下載

2007-05-22 15:54:01 | Add Comment

快速上手 - Lucene

Lucene是一個開放原始碼的資訊檢索函式庫,最初是由 Doug Cutting 利用Java程式實作完成的,它目前是 Apache Software Foundation 底下的計畫之一,由於相當的受到肯定,所以也洐生出其它的程式版本,如:Perl, C#, C++, Python, Ruby and PHP。

建立Index

一個個搜尋檔案的方式,其實是相當耗時且緩慢的,所以我們必須先為我們想要搜尋的文件建立索引,就如同書籍中提供在最後面的索引頁,可以幫助我們更快速的找到我們想要知道的東西。

Step 1:假設我們想要索引的文件放置在「D:\index」

Step 2:切換工作目錄到你的lucene目錄夾,並輸入下列指令

D:\lucene-2.1.0>java -cp .;lucene-core-2.1.0.jar;lucene-demos-2.1.0.jar org.apache.lucene.demo.IndexFiles d:\index

若出現以下類似的訊息即代表已成功索引,並會在此目錄下建立一個「index」的資料夾,底下包含了三個檔案,簡單來說就是已被索引的資訊。

Indexing to directory 'index'...
adding d:\index\Wang.txt
adding d:\index\Apollo.txt
adding d:\index\JavaFX.txt
adding d:\index\Silverlight.txt
Optimizing...
500 total milliseconds

Command搜尋

接著我們要搜尋剛剛被我們索引的那些文件中的內容~請輸入下列指令

D:\lucene-2.1.0>java -cp .;lucene-core-2.1.0.jar;lucene-demos-2.1.0.jar org.apache.lucene.demo.SearchFiles

接著會出現「Query:」的提示字元,你就可以輸入你想要搜尋的資訊囉~

例如:

Searching for: ajax
2 total matching documents
1. d:\index\Silverlight.htm
2. d:\index\Apollo.htm

Web搜尋

位於lucene目錄底下有一個「luceneweb.war」檔,請將它拷貝至你的Application Server的部署目錄下,例如:Tomcat or JBoss 等…

以Tomcat為例,啟動你的Tomcat之後,它會將此檔解壓縮成luceneweb的目錄夾,接著請修改此目錄夾底下的「configuration.jsp」如下所示:

//String indexLocation = "/opt/lucene/index";
String indexLocation = "D:\\lucene-2.1.0\\index";

也就是設定剛剛索引文件的目錄位置即可,最後開啟http://localhost:8080/luceneweb/就開始享受Lucene吧~

2007-05-18 17:56:03 | Add Comment

Next Posts~:::~Previous Posts
Copyright (C) Ching-Shen Chen. All rights reserved.

::: 搜尋 :::

::: 分類 :::

::: 最新文章 :::

::: 最新回應 :::

::: 訂閱 :::

Atom feed
Atom Comment