blog.Ring.idv.tw

Articles

事件處理-「MovieClip」、「Button」對於「on」事件處理的變數範圍

「MovieClip」、「Button」對於「on」事件處理的變數範圍

我們建立一個MovieClip及一個Button在Stage上,並將MovieClip命名為「a_mc」,然後在MovieClip及Button上各別加些程式以便驗證。

MovieClip

on(release)
{
	bar = 10;
	trace(this);
}

Button

on(release)
{
	foo = 5;
	trace(this);
}

最後再額外多建立一個Button,用來驗證變數的範圍~

測試用的Button

on(release)
{
	trace(foo);
	trace(a_mc.bar);
}

接著依序點擊「MovieClip」、「Button」以及「測試用的Button」,結果如下:

_level0.a_mc
_level0
5
10

從結果我們就可以了解,寫在「MovieClip」和「Button」的變數範圍是不一樣的,寫在「MovieClip」的變數範圍就如同「事件處理-「onClipEvent」vs.「MovieClip.onXXX」變數範圍」這篇所說明的,相對來說「Button」的情況較為特殊,也就是說寫在「Button」上的變數範圍,其實就是隸屬於「_root」的實體變數,更明確的說「_root」相等於MainTimeline的「this」,然而MainTimeline的「this」又是個「MovieClip」~

關於MainTimeline中的「this」,可以參考「ActionScript 2 this vs. ActionScript 3 this」這篇的說明~

範例下載

2007-05-25 18:18:21 | Add Comment

你長得像哪位名人呢?

剛剛在朋友的Blog中發現~ 有一個做Face Recognition的網站MyHeritage,可以提供將自己的照片上傳,然後此系統便會自動幫你分析,看看你長得像哪位名人哦~

現在才發現~我居然和「梁朝偉」先生是有所淵源地~ 呵~

2007-05-23 19:01:49 | Add Comment

什麼是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

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

::: 分類 :::

::: 最新文章 :::

::: 最新回應 :::