2011年11月11日00:00
it168網(wǎng)站原創(chuàng ) 作者:DoubleLife 編輯:
皮麗華 我要評論(0)標簽:
Java ,
javascript【IT168 專(zhuān)稿】2011年10月6日,一年一度的JavaOne大會(huì )隆重舉行。JavaOne2011大會(huì )的主題之一介紹針對不同Java平臺的產(chǎn)品路線(xiàn)圖,這其中包括移動(dòng)版(ME,Micro Edition)、標準版(SE,Standard Edition)以及企業(yè)版(EE,Enterprise Edition)。
Java SE的亮點(diǎn)之一就是Oracle詳細闡述Java SE 8路線(xiàn)圖。我們先來(lái)看看Java SE 8新增了哪些主要功能:
Java SE 8 新增主要功能
1、為提高開(kāi)發(fā)人員工作效率,更好地利用多核處理器和增強對Java集合APIs的大容量數據處理功能,推出的Lambda表達式(“關(guān)閉”)。
2、原始Java模塊系統(“項目Jigsaw”)將簡(jiǎn)化應用程序的構建、包裝以及部署,讓一個(gè)完全模塊化的Java平臺能在
服務(wù)器、客戶(hù)和嵌入式系統上進(jìn)行定制化部署。
3、在JVM上的JavaScript改進(jìn),包括一個(gè)為JVM優(yōu)化的全新JavaScript引擎Nashorn和全面的Java / JavaScript互操作性。
4、具有JavaFX 3.0形式的下一代Java客戶(hù)端。包括多點(diǎn)觸摸功能的現代設備支持。
5、完成的HotSpot / JRockit JVM集聚項目,包括性能增強和第二代的Java Flight Recorder。
其中,筆者最關(guān)心的是第三條,即JVM對JavaScript的改進(jìn)。它的核心組件是JavaScript引擎Nashorn,它實(shí)現了Java與JavaScript互操作性。Nashorn一詞與Rhino類(lèi)似,漢語(yǔ)意思均為犀牛。而巧合的是,Rhino就是JavaScript引擎,它的目的就是實(shí)現Java與JavaScript的互操作性。那么Rhino究竟是什么呢?為什么說(shuō)Nashorn是新一代JavaScript引擎?Rhino有什么特性?Rhino與Java及JavaScript有什么關(guān)系呢?本文將會(huì )為您一一解答。
什么是Rhino?
Rhino 是 JavaScript 的一種基于Java的實(shí)現,原先由Mozilla開(kāi)發(fā),現在被集成進(jìn)入JDK 6.0。下面這兩行代碼恰好說(shuō)明了這一點(diǎn)。
import sun.org.mozilla.javascript.internal.Context;
import sun.org.mozilla.javascript.internal.Scriptable;
Rhino漢語(yǔ)意思為犀牛,它的名字來(lái)源于 O'Reilly 關(guān)于 JavaScript 的書(shū)的封面,如圖一所示。
圖一 “犀?!暗膩?lái)源
Rhino的特點(diǎn)如下:
JavaScript 1.5的全部特性
允許使用腳本直接操作Java
提供JavaScript Shell執行其它JavaScript腳本
提供JavaScript編譯器將JavaScript源程序轉換成Java類(lèi)文件
Rhino相關(guān)背景
Rhino的歷史可追溯到1997 年。Netscape計劃開(kāi)發(fā)Java版的Navigator,即Javagator。它也就是 Rhino 的前身。雖然 Javagator 未能開(kāi)花結果,但是Rhino,作為Netscape 對 JavaScript 的移植語(yǔ)言,經(jīng)過(guò)時(shí)間考驗存活了下來(lái)。
如今,隨著(zhù) Rhino 開(kāi)放源代碼,越來(lái)越多的開(kāi)發(fā)者參與了 Rhino 的開(kāi)發(fā)。隨著(zhù)Rhino的愈加成熟,越來(lái)越多的用戶(hù)選擇使用了Rhino。
Rhino語(yǔ)言特點(diǎn)
Java是一種面對對象的編譯型語(yǔ)言。它首先將源代碼編譯成二進(jìn)制字節碼(bytecode),然后依賴(lài)各種不同平臺上的虛擬機來(lái)解釋執行字節碼,從而實(shí)現了“一次編譯、到處執行”的跨平臺特性。
JavaScript是一種動(dòng)態(tài)、弱類(lèi)型、基于原型的客戶(hù)端腳本語(yǔ)言。JavaScript 包括一個(gè)基于對象的 API,稱(chēng)為文檔對象模型(Document Object Model)或 DOM,用以訪(fǎng)問(wèn)和操作 Web 頁(yè)面的內容,給HTML網(wǎng)頁(yè)添加動(dòng)態(tài)功能。
Rhino是一個(gè)介于Java與JavaScript之間的語(yǔ)言。它的基礎是 Java 語(yǔ)言,這使得它簡(jiǎn)單易學(xué),但相比于JavaScript腳本語(yǔ)言來(lái)說(shuō),它又太過(guò)復雜。不過(guò),Rhino 的主要缺點(diǎn)也正是它的強大之處,Rhino 是一種輕量級的、功能強大的腳本語(yǔ)言。Rhino 使用原型而不是類(lèi),這使它比很多腳本語(yǔ)言更適合開(kāi)發(fā) GUI 應用程序,在考慮性能和風(fēng)格等因素時(shí)更是如此。
Rhino語(yǔ)言特點(diǎn)的優(yōu)缺點(diǎn)
一方面,作為一種動(dòng)態(tài)類(lèi)型的、基于原型的腳本語(yǔ)言,Rhino借用了很多JavaScript語(yǔ)法。比如,Rhino不再使用語(yǔ)句結束符( ; ),放寬了變量聲明規則,并且極大地簡(jiǎn)化了修改和檢索對象屬性的語(yǔ)法。另一方面,作為JavaScript 的Java實(shí)現,Rhino語(yǔ)法非常類(lèi)似于Java編程語(yǔ)言。比如,Rhino采用了與 Java 編程語(yǔ)言相似的循環(huán)和條件結構,并且遵循類(lèi)似的語(yǔ)法模式來(lái)表示這些結構。
Rhino 和 Java 語(yǔ)言之間有一些顯著(zhù)的區別。Rhino 是一種基于原型的(prototype-based)語(yǔ)言,而不是一種基于類(lèi)的(class-based)語(yǔ)言。Rhino中,函數和變量的聲明中看不到類(lèi)型,取而代之的是,使用 function關(guān)鍵字聲明函數,使用 var關(guān)鍵字聲明局部變量。
Rhino的原始想法是將JavaScript 編譯成Java字節碼執行,即采用編譯執行的方式。由于由于JVM存在垃圾收集、編譯和裝載過(guò)程的開(kāi)銷(xiāo)過(guò)大等限制,Rhino采用了解釋執行的方式。
如何下載Rhino安裝包
用戶(hù)可以從官網(wǎng)
http://www.mozilla.org/rhino/ 下載Rhino,筆者下載的版本為rhino1.7R3.zip。
其中,主要的目錄與文件的如下:
Src:Rhino相關(guān)Jar包對應的源代碼
Javadoc:Rhino相關(guān)Jar包對應的Java說(shuō)明文檔
Examples:Rhino相關(guān)示例
build.xml:Rhino工程對應的Ant文件
js.jar:Rhino對應的Jar包
【IT168 專(zhuān)稿】Rhino環(huán)境配置
在使用之前,我們需要配置環(huán)境及運行js腳本。具體如下:
1、 將下載包中的js.jar文件加入系統CLASSPATH中。
2、 運行js解釋器java org.mozilla.javascript.tools.shell.Main。進(jìn)入交互模式:
Rhino 1.7 release 3 2011 05 09
js>
注:第一行為js解釋器的版本號,后面跟著(zhù)提示符 js>
下面我們將利用js shell,使用JavaScript操縱Java對象。
JavaScript操縱Java對象
1. Rhino如何訪(fǎng)問(wèn)Java包與類(lèi)文件
Java語(yǔ)法規定,任何代碼都必須以class文件的形式存在,而每個(gè)class文件必須屬于一個(gè)package,默認為default。而JavaScript并沒(méi)有類(lèi)似package的層級結構概念,那么如何使用Rhino訪(fǎng)問(wèn)Java類(lèi)文件呢?
Rhino定義了一個(gè)top-level變量Packages。變量Packages對應的所有屬性均對應Java包名。比如,我們需要訪(fǎng)問(wèn)某一個(gè)Java的Package com.example.
js> Packages.com.example
[JavaPackage com.example]
簡(jiǎn)單起見(jiàn),我們也可以去掉變量Packages,直接輸入Java包名。因此,上述Package com.example等價(jià)與com.example,如下:
js> com.example
[JavaPackage com.example]
剛才演示了如何通過(guò)js shell訪(fǎng)問(wèn)Java包,訪(fǎng)問(wèn)Java類(lèi)的方式類(lèi)似。假如我們需要訪(fǎng)問(wèn)標準的Java 文件類(lèi)java.io.File,如下。
js> java.io.File
[JavaClass java.io.File]
或者,為避免輸入全名,我們先導入包,然后輸入Class類(lèi)名,如下:
js> importPackage(java.io)
js> File
[JavaClass java.io.File]
這里的importPackage(java.io),在效果上等價(jià)于Java聲明import java.io.*; 不同的是,Java會(huì )隱式import java.lang.*,而Rhino不會(huì )。因為Rhino定義的對象Boolean, Math, Number, Object, String等與Java語(yǔ)法完全不同,兩者無(wú)法等價(jià)。
這里需要注意的是,Rhino對該語(yǔ)法的錯誤處理機制,當被訪(fǎng)問(wèn)的類(lèi)存在時(shí),Rhino加載該class,而當其不存在時(shí),則把它當成package名稱(chēng),而并不會(huì )報錯。例如,當訪(fǎng)問(wèn)一個(gè)不存在的類(lèi)com.example.AAA時(shí),輸入如下。
js> com.example.AAA
[JavaPackage com.example.AAA]
僅當訪(fǎng)問(wèn)類(lèi)AAA時(shí),Rhino才會(huì )報錯。
2. Rhino如何與Java對象交互
與Java類(lèi)似,Rhino使用new操作符創(chuàng )建對象。
js> new java.util.Date()
Thu Nov 03 16:19:04 CST 2011
可以使用JavaScript變量存儲Java對象,并調用其方法,如下:
js> f = new java.io.File("sample.txt")
sample.txt
js> f.isDirectory()
false
對于static方法與變量,調用如下:
js> java.lang.Math.PI
3.141592653589793
js> java.lang.Math.cos(0)
1
【IT168 專(zhuān)稿】在JavaScript中,方法本身就是對象,這一點(diǎn)與Java不同。我們可以通過(guò)下列方式查看方法的重載:
js> f.listFiles
function listFiles() {/*
java.io.File[] listFiles()
java.io.File[] listFiles(java.io.FilenameFilter)
java.io.File[] listFiles(java.io.FileFilter)
*/}
輸出中列出三個(gè)重載方法。第一個(gè)為無(wú)參函數,第二與第三個(gè)對應的參數分別為FilenameFilter與FileFilter。
另一個(gè)比較有意思的特點(diǎn)是通過(guò)構造for..in,查看對象對應的所有方法與變量。如下:
js> for (i in f) { print(i) }
exists
parentFile
mkdir
toString
wait
[44 others]
這里列出的方法一部分來(lái)自于父類(lèi),比如wait來(lái)自父類(lèi)java.lang.Object。
對于JavaBean,Rhino也提供按名字訪(fǎng)問(wèn)的簡(jiǎn)單方式。比如,通過(guò)下面這種方式,我們就可以調用File對象的getName與isDirectory方法:
js> f.name
test.txt
js> f.directory
false
3. Rhino如何實(shí)現Java接口
JavaScript當中,方法本身就是對象。下面我們通過(guò)JavaScript語(yǔ)法{propertyName: value}聲明一個(gè)JavaScript方法,并調用該方法如下:
js> obj = { run: function () { print("\nrunning"); } }
[object Object]
js> obj.run()
running
現在我們構造一個(gè)JavaScript對象,實(shí)現Runnable接口。并將該對象作為參數,構造一個(gè)新的線(xiàn)程,并啟動(dòng)該線(xiàn)程。
js> r = new java.lang.Runnable(obj);
adapter1@291aff
js> t = new java.lang.Thread(r)
Thread[Thread-0,5,main]
js> t.start()
js>
running
最后的js>提示符與新線(xiàn)程的打印輸出running的先后順序是隨機的,取決于線(xiàn)程的調度策略。
從后端的處理流程來(lái)講,Rhino首先為Runnable接口的實(shí)現類(lèi)生成Java字節碼文件。然后調用JavaScript對象定義的Run方法。
4. Rhino如何創(chuàng )建Java 數組
Rhino使用Java的發(fā)射機制生成數組。下面是生成2個(gè)String對象的代碼:
js> array = java.lang.reflect.Array.newInstance(java.lang.String, 2);
[Ljava.lang.String;@a20892
js> array[0] = "Double"
Double
js> array[1] = "Life"
Life
js> array[0] + array[1]
DoubleLife
js>
5. Rhino如何捕獲與處理異常
與Java類(lèi)似,Rhino使用try...catch關(guān)鍵字處理異常。
js> function classForName(name) {
try {
return java.lang.Class.forName(name);
} catch (e if e.javaException instanceof java.lang.ClassNotFoundException) {
print("Class " + name + " not found");
} catch (e if e.javaException instanceof java.lang.NullPointerException) {
print("Class name is null");
}
} > > > > > > > >
js> classForName("NonExistingClass");
Class NonExistingClass not found
js> classForName(null);
Class name is null
【IT168 專(zhuān)稿】6. Rhino如何調用js文件
當然,除了在命令行的方式,我們還可以使用操縱JavaScript文件。下面是一段JavaScript代碼,主要目的是判斷該數是否為質(zhì)數。代碼如下:
function isPrime (num)
{
if (num <= 1) {
print("Enter an integer no less than 2.")
return false
}
var prime = true
var sqrRoot = Math.round(Math.sqrt(num))
for (var n = 2; prime & n <= sqrRoot; ++n) {
prime = (num % n != 0)
}
return prime
}
我們保存文件為C:\isPrime.js。然后我們需要調用load方法加載該腳本。最后,我們可以調用isPrime方法來(lái)判斷是否為質(zhì)數。
js> load("C:/isPrime.js")
js> isPrime(33);
false
js> isPrime(31)
true
需要注意的是,注意:文件分隔符需要調整,是“/”而不是“\”。
上述部分示例可以參見(jiàn)Rhino官方網(wǎng)站
https://developer.mozilla.org/en/Scripting_Java。另外examples目錄下很多例子都值得參考與學(xué)習。
剛才使用JavaScript操縱Java對象。接下來(lái)我們看看如何使用Java程序訪(fǎng)問(wèn)JavaScript
Java對象操縱JavaScript
下面是一段Java代碼,用來(lái)運行數學(xué)表達式。代碼如下:
package com.example;
import sun.org.mozilla.javascript.internal.Context;
import sun.org.mozilla.javascript.internal.Scriptable;
publicclass Test {
publicstaticvoid main(String[] args) {
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
String str = "3/(1+2)";
Object result = cx.evaluateString(scope, str, null, 1, null);
System.out.println(str + "=" + Context.toNumber(result));
} finally {
Context.exit();
}
}
}
運行Java com.example.Test,輸出結果如下:
3/(1+2)=1.0
之所以是1.0而不是1,是因為Context.toNumber(result)返回的類(lèi)型為double。另一個(gè)值得注意的是,這里import的package屬于JDK 6.0。因此,在不需要Rhino提供的js.jar,該程序仍能獨立運行。
雖然Rhino作為JavaScript運行時(shí),功能非常強大,但在性能上卻無(wú)法與其他的JavaScript運行時(shí)(比如Google Chrome的V8 JavaScript Engine)相提并論。值得注意的是,JRuby專(zhuān)家Charles Oliver Nutter也開(kāi)始參與Rhino項目中,以提升Rhino JavaScript運行時(shí)的速度,進(jìn)而實(shí)現與V8的競爭。而Oracle在對JVM上的JavaScript改進(jìn)與優(yōu)化,我們有理由期待,在未來(lái),新一代JavaScript運行時(shí)Nashorn的速度將會(huì )得到極大的提升。
參考資料:
http://www.mozilla.org/rhino/