欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
給 Java SE 注入腳本語(yǔ)言的活力

給 Java SE 注入腳本語(yǔ)言的活力

Mustang 的腳本語(yǔ)言新特性

級別: 中級

吳 玥顥 (wuyuehao@cn.ibm.com), 軟件工程師, IBM

2006 年 10 月 30 日

在即將發(fā)布的 Java SE6(Mustang)中,增加了對腳本語(yǔ)言的支持。通過(guò)對腳本語(yǔ)言的調用,使得一些通常用 Java 比較難于實(shí)現的功能變得簡(jiǎn)單和輕便。腳本語(yǔ)言與 Java 之間的互操作將變得優(yōu)雅而直接。

腳本語(yǔ)言與 Java

假設我們有一個(gè)簡(jiǎn)單的需求,察看一份文檔中 5 個(gè)字母組成的單詞的個(gè)數。用 Java 一般實(shí)現如下:

import java.io.BufferedReader;                        import java.io.FileReader;                        import java.io.IOException;                        public class Find5Words {                        public static void main(String[] args) throws IOException {                        String result = "";                        String line = null;                        int num = 0;                        FileReader fr = new FileReader("filename");                        BufferedReader br = new BufferedReader(fr);                        while ((line = br.readLine()) != null) {                        result += line;                        }                        br.close();                        fr.close();                        String[] s = result.split(" ");                        for (int i = 0; i < s.length; i++) {                        if (s[i].matches("^\\w{5}$")) {                        num++;                        }                        }                        System.out.println(num);                        }                        }                        

再看看 Perl 語(yǔ)言實(shí)現同樣功能的代碼:

open FILE, "<filename ";                        while (<FILE>) {                        for (split) {                        $num++ if /^\w{5}$/                        }                        }                        print $num;                        

那么有沒(méi)有一種優(yōu)雅的方式將 Java 與腳本語(yǔ)言結合呢,在今年秋季即將發(fā)布的 Java SE6(代號 Mustang)中,這將成為現實(shí)。





回頁(yè)首


Mustang 的腳本引擎

JSR 233 為 Java 設計了一套腳本語(yǔ)言 API。這一套 API 提供了在 Java 程序中調用各種腳本語(yǔ)言引擎的接口。任何實(shí)現了這一接口的腳本語(yǔ)言引擎都可以在 Java 程序中被調用。在 Mustang 的發(fā)行版本中包括了一個(gè)基于 Mozilla Rhino 的 JavaScript 腳本引擎。

Mozilla Rhino

Rhino 是一個(gè)純 Java 的開(kāi)源的 JavaScript 實(shí)現。他的名字來(lái)源于 O‘Reilly 關(guān)于 JavaScript 的書(shū)的封面:

Rhino 項目可以追朔到 1997 年,當時(shí) Netscape 計劃開(kāi)發(fā)一個(gè)純 Java 實(shí)現的 Navigator,為此需要一個(gè) Java 實(shí)現的 JavaScript —— Javagator。它也就是 Rhino 的前身。起初 Rhino 將 JavaScript 編譯成 Java 的二進(jìn)制代碼執行,這樣它會(huì )有最好的性能。后來(lái)由于編譯執行的方式存在垃圾收集的問(wèn)題并且編譯和裝載過(guò)程的開(kāi)銷(xiāo)過(guò)大,不能滿(mǎn)足一些項目的需求,Rhino 提供了解釋執行的方式。隨著(zhù) Rhino 開(kāi)放源代碼,越來(lái)越多的用戶(hù)在自己的產(chǎn)品中使用了 Rhino,同時(shí)也有越來(lái)越多的開(kāi)發(fā)者參與了 Rhino 的開(kāi)發(fā)并做出了很大的貢獻。如今 Rhino1.6R2 版本將被包含在 Java SE6 中發(fā)行,更多的 Java 開(kāi)發(fā)者將從中獲益。

Rhino 提供了如下功能

  • 對 JavaScript 1.5 的完全支持
  • 直接在 Java 中使用 JavaScript 的功能
  • 一個(gè) JavaScript shell 用于運行 JavaScript 腳本
  • 一個(gè) JavaScript 的編譯器,用于將 JavaScript 編譯成 Java 二進(jìn)制文件

支持的腳本語(yǔ)言

dev.java.net可以找到官方的腳本引擎的實(shí)現項目。這一項目基于BSD License ,表示這些腳本引擎的使用將十分自由。目前該項目已對包括 Groovy, JavaScript, Python, Ruby, PHP 在內的二十多種腳本語(yǔ)言提供了支持。這一支持列表還將不斷擴大。

在 Mustang 中對腳本引擎的檢索使用了工廠(chǎng)模式。首先需要實(shí)例化一個(gè)工廠(chǎng) —— ScriptEngineManager。

// create a script engine manager                        ScriptEngineManager factory = new ScriptEngineManager();                        

ScriptEngineManager 將在 Thread Context ClassLoader 的 Classpath 中根據 jar 文件的 META-INF 來(lái)查找可用的腳本引擎。它提供了 3 種方法來(lái)檢索腳本引擎:

// create engine by name                        ScriptEngine engine = factory.getEngineByName ("JavaScript");                        // create engine by name                        ScriptEngine engine = factory.getEngineByExtension ("js");                        // create engine by name                        ScriptEngine engine = factory.getEngineByMimeType ("application/javascript");                        

下面的代碼將會(huì )打印出當前的 JDK 所支持的所有腳本引擎

ScriptEngineManager factory = new ScriptEngineManager();                        for (ScriptEngineFactory available : factory.getEngineFactories()) {                        System.out.println(available.getEngineName());                        }                        

以下各章節代碼將以 JavaScript 為例。

在 Java 中解釋腳本

有了腳本引擎實(shí)例就可以很方便的執行腳本語(yǔ)言,按照慣例,我們還是從一個(gè)簡(jiǎn)單的 Hello World 開(kāi)始:

public class RunJavaScript {                        public static void main(String[] args){                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName ("JavaScript");                        engine.eval("print(‘Hello World‘)");                        }                        }                        

這段 Java 代碼將會(huì )執行 JavaScript 并打印出 Hello World。如果 JavaScript 有語(yǔ)法錯誤將會(huì )如何?

engine.eval("if(true){println (‘hello‘)");                        

故意沒(méi)有加上”}”,執行這段代碼 Java 將會(huì )拋出一個(gè) javax.script.ScriptException 并準確的打印出錯信息:
Exception in thread "main" javax.script.ScriptException:                        sun.org.mozilla.javascript.internal.EvaluatorException:                        missing } in compound statement (<Unknown source>#1) in <Unknown source>                        at line number 1                        at ...                        

如果我們要解釋一些更復雜的腳本語(yǔ)言,或者想在運行時(shí)改變該腳本該如何做呢?腳本引擎支持一個(gè)重載的 eval 方法,它可以從一個(gè) Reader 讀入所需的腳本:

ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName ("JavaScript");                        engine.eval(new Reader("HelloWorld.js"));                        

如此這段 Java 代碼將在運行時(shí)動(dòng)態(tài)的尋找 HelloWorld.js 并執行,用戶(hù)可以隨時(shí)通過(guò)改變這一腳本文件來(lái)改變 Java 代碼的行為。做一個(gè)簡(jiǎn)單的實(shí)驗,Java 代碼如下:
public class RunJavaScript {                        public static void main(String[] args) throws FileNotFoundException,                        ScriptException, InterruptedException {                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName ("JavaScript");                        while (true) {                        engine.eval(new FileReader("HelloWorld.js"));                        Thread.sleep(1000);                        }                        }                        }                        

HelloWorld.js 內容為簡(jiǎn)單的打印一個(gè) Hello World: print(‘Hello World‘);

運行 RunJavaScript 將會(huì )每一秒鐘打印一個(gè) Hello World。這時(shí)候修改 HelloWorld.js 內容為 print(‘Hello Tony‘);

打印的內容將變?yōu)?Hello Tony,由此可見(jiàn) Java 程序將動(dòng)態(tài)的去讀取腳本文件并解釋執行。對于這一簡(jiǎn)單的 Hello World 腳本來(lái)說(shuō),IO 操作將比直接執行腳本損失 20% 左右的性能(在我的 Think Pad 上),但他帶來(lái)的靈活性——在運行時(shí)動(dòng)態(tài)改變代碼的能力,在某些場(chǎng)合是十分激動(dòng)人心的。

腳本語(yǔ)言與 Java 的通信

ScriptEngine 的 put 方法用于將一個(gè) Java 對象映射成一個(gè)腳本語(yǔ)言的變量?,F在有一個(gè) Java Class,它只有一個(gè)方法,功能就是打印一個(gè)字符串 Hello World:

package tony;                        public class HelloWorld {                        String s = "Hello World";                        public void sayHello(){                        System.out.println(s);                        }                        }                        

那么如何在腳本語(yǔ)言中使用這個(gè)類(lèi)呢?put 方法可以做到:

import javax.script.ScriptEngine;                        import javax.script.ScriptEngineManager;                        import javax.script.ScriptException;                        public class TestPut {                        public static void main(String[] args) throws ScriptException {                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName("JavaScript");                        HelloWorld hello = new HelloWorld();                        engine.put("script_hello", hello);                        engine.eval("script_hello.sayHello()");                        }                        }                        

首先我們實(shí)例化一個(gè) HelloWorld,然后用 put 方法將這個(gè)實(shí)例映射為腳本語(yǔ)言的變量 script_hello。那么我們就可以在 eval() 函數中像 Java 程序中同樣的方式來(lái)調用這個(gè)實(shí)例的方法。同樣的,假設我們有一個(gè)腳本函數,它進(jìn)行一定的計算并返回值,我們在 Java 代碼中也可以方便的調用這一腳本:

package tony;                        import javax.script.Invocable;                        import javax.script.ScriptEngine;                        import javax.script.ScriptEngineManager;                        import javax.script.ScriptException;                        public class TestInv {                        public static void main(String[] args) throws ScriptException,                        NoSuchMethodException {                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName("JavaScript");                        String script = "function say(first,second) { print(first +‘ ‘+ second); }";                        engine.eval(script);                        Invocable inv = (Invocable) engine;                        inv.invokeFunction("say", "Hello", "Tony");                        }                        }                        

在這個(gè)例子中我們首先定義了一個(gè)腳本函數 say,它的作用是接受兩個(gè)字符串參數將他們拼接并返回。這里我們第一次遇到了 ScriptEngine 的兩個(gè)可選接口之一 —— Invocable,Invocable 表示當前的 engine 可以作為函數被調用。這里我們將 engine 強制轉換為 Invocable 類(lèi)型,使用 invokeFunction 方法將參數傳遞給腳本引擎。invokeFunction這個(gè)方法使用了可變參數的定義方式,可以一次傳遞多個(gè)參數,并且將腳本語(yǔ)言的返回值作為它的返回值。下面這個(gè)例子用JavaScript實(shí)現了一個(gè)簡(jiǎn)單的max函數,接受兩個(gè)參數,返回較大的那個(gè)。為了便于斷言結果正確性,這里繼承了JUnit Testcase,關(guān)于JUnit請參考www.junit.org。

package tony;                        import javax.script.Invocable;                        import javax.script.ScriptEngine;                        import javax.script.ScriptEngineManager;                        import javax.script.ScriptException;                        import junit.framework.TestCase;                        public class TestScripting extends TestCase {                        public void testInv() throws ScriptException, NoSuchMethodException {                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName("JavaScript");                        String script = "function max(first,second) "                        + "{ return (first > second) ?first:second;}";                        engine.eval(script);                        Invocable inv = (Invocable) engine;                        Object obj = inv.invokeFunction("max", "1", "0");                        assertEquals("1", obj.toString());                        }                        }                        

Invocable 接口還有一個(gè)方法用于從一個(gè) engine 中得到一個(gè) Java Interface 的實(shí)例,它的定義如下:

<T> T getInterface(Class<T> clasz)                        

它接受一個(gè) Java 的 Interface 類(lèi)型作為參數,返回這個(gè) Interface 的一個(gè)實(shí)例。也就是說(shuō)你可以完全用腳本語(yǔ)言來(lái)寫(xiě)一個(gè) Java Interface 的所有實(shí)現。以下是一個(gè)例子。首先定一了個(gè) Java Interface,它有兩個(gè)簡(jiǎn)單的函數,分別為求最大值和最小值:

package tony;                        public interface MaxMin {                        public int max(int a, int b);                        public int min(int a, int b);                        }                        

這個(gè) Testcase 用 JavaScript 實(shí)現了 MaxMin 接口,然后用 getInterface 方法返回了一個(gè)實(shí)例并驗證了結果。

public void testInvInterface() throws ScriptException,                        NoSuchMethodException {                        ScriptEngineManager factory = new ScriptEngineManager();                        ScriptEngine engine = factory.getEngineByName("JavaScript");                        String script = "function max(first,second) "                        + "{ return (first > second) ?first:second;}";                        script += "function min(first,second) { return (first < second) ?first:second;}";                        engine.eval(script);                        Invocable inv = (Invocable) engine;                        MaxMin maxMin = inv.getInterface(MaxMin.class);                        assertEquals(1, maxMin.max(1, 0));                        assertEquals(0, maxMin.min(1, 0));                        }                        

腳本的編譯執行

到目前為止,我們的腳本全部都是解釋執行的,相比較之下編譯執行將會(huì )獲得更好的性能。這里將介紹 ScriptEngine 的另外一個(gè)可選接口 —— Compilable,實(shí)現了這一接口的腳本引擎支持腳本的編譯執行。下面這個(gè)例子實(shí)現了一個(gè)判斷給定字符串是否是 email 地址或者 ip 地址的腳本:

public void testComplie() throws ScriptException {                        ScriptEngineManager manager = new ScriptEngineManager();                        ScriptEngine engine = manager.getEngineByName("JavaScript");                        String script = "var email=/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]"                        + "+(\\.[a-zA-Z0-9_-]+)+$/;";                        script += "var ip = /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])"                        +"(\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])){3}$/;";                        script += "if(email.test(str)){println(‘it is an email‘)}"                        + "else if(ip.test(str)){println(‘it is an ip address‘)}"                        + "else{println(‘I don\\‘t know‘)}";                        engine.put("str", "email@address.tony");                        Compilable compilable = (Compilable) engine;                        CompiledScript compiled = compilable.compile(script);                        compiled.eval();                        }                        

腳本編譯的過(guò)程如下:首先將 engine 轉換為 Compilable 接口,然后調用 Compilable 接口的 compile 方法得到一個(gè) CompiledScript 的實(shí)例,這個(gè)實(shí)例就代表一個(gè)編譯過(guò)的腳本,如此用 CompiledScript 的 eval 方法即為調用編譯好的腳本了。在我的 Think Pad 上,這段代碼編譯后的調用大約比直接調用 engine.eval 要快 3-4 倍。隨著(zhù)腳本復雜性的提升,性能的提升會(huì )更加明顯。

腳本上下文與綁定

真正將腳本語(yǔ)言與 Java 聯(lián)系起來(lái)的不是 ScriptEngine,而是 ScriptContext,它作為 Java 與 ScriptEngine 之間的橋梁而存在。

一個(gè) ScriptEngine 會(huì )有一個(gè)相應的 ScriptContext,它維護了一個(gè) Map,這個(gè) Map 中的每個(gè)元素都是腳本語(yǔ)言對象與 Java 對象之間的映射。同時(shí)這個(gè) Map 在我們的 API 中又被稱(chēng)為 Bindings。一個(gè) Bindings 就是一個(gè)限定了 key 必須為 String 類(lèi)型的 Map —— Map<String, Object>。所以一個(gè) ScriptContext 也會(huì )有對應的一個(gè) Bindings,它可以通過(guò) getBindings 和 setBindings 方法來(lái)獲取和更改。

一個(gè) Bindings 包括了它的 ScriptContext 中的所有腳本變量,那么如何獲取腳本變量的值呢?當然,從 Bindings 中 get 是一個(gè)辦法,同時(shí) ScriptContext 也提供了 getAttribute 方法,在只希望獲得某一特定腳本變量值的時(shí)候它顯然是十分有效的。相應地 setAttribute 和 removeAttribute 可以增加、修改或者刪除一個(gè)特定變量。

在 ScriptContext 中存儲的所有變量也有自己的作用域,它們可以是 ENGINE_SCOPE 或者是 GLOBAL_SCOPE,前者表示這個(gè) ScriptEngine 獨有的變量,后者則是所有 ScriptEngine 共有的變量。例如我們執行 engine.put(key, value) 方法之后,這時(shí)便會(huì )增加一個(gè) ENGINE_SCOPE 的變量,如果要定義一個(gè) GLOBAL_SCOPE 變量,可以通過(guò) setAttribute(key, value, ScriptContext.GLOBAL_SCOPE) 來(lái)完成。

此外 ScriptContext 還提供了標準輸入和輸出的重定向功能,它可以用于指定腳本語(yǔ)言的輸入和輸出。





回頁(yè)首


在 JavaScript 中使用 Java 高級特性

這一部分不同于前述內容,將介紹 JavaScript引擎 —— Rhino 獨有的特性。

使用 Java 對象

前面的部分已經(jīng)介紹過(guò)如何在 JavaScript 中使用一個(gè)已經(jīng)實(shí)例化的 Java 對象,那么如何在 JavaScript 中去實(shí)例化一個(gè) Java 對象呢?在 Java 中所有 Class 是按照包名分層次存放的,而在 JavaScript 沒(méi)有這一結構,Rhino 使用了一個(gè)巧妙的方法實(shí)現了對所有 Java 對象的引用。Rhino 中定義了一個(gè)全局變量—— Packages,并且它的所有元素也是全局變量,這個(gè)全局變量維護了 Java 類(lèi)的層次結構。例如 Packages.java.io.File 引用了 Java 的 io 包中 File 對象。如此一來(lái)我們便可以在 JavaScript 中方便的使用 Java 對象了,new 和 Packages 都是可以被省略的:

//The same as: var frame = new Packages.java.io.File("filename");                        var frame = java.io.File("filename");                        

我們也可以像 Java 代碼中一樣把這個(gè)對象引用進(jìn)來(lái):

importClass (java.io.File);                        var file = File("filename");                        

如果要將整個(gè)包下的所有類(lèi)都引用進(jìn)來(lái)可以用 importPackage:

importPackage(java.io);                        

如果只需要在特定代碼段中引用某些包,可以使用 JavaImporter 搭配 JavaScript 的 with 關(guān)鍵字,如:

var MyImport = JavaImporter(java.io.File);                        with (MyImport) {                        var myFile = File("filename");                        }                        

用戶(hù)自定義的包也可以被引用進(jìn)來(lái),不過(guò)這時(shí)候 Packages 引用不能被省略:

importPackage(Packages.tony);                        var hello = HelloWorld();                        hello.sayHello();                        

注意這里只有 public 的成員和方法才會(huì )在 JavaScript 中可見(jiàn),例如對 hello.s 的引用將得到 undefined。下面簡(jiǎn)單介紹一些常用的特性:

使用 Java 數組

需要用反射的方式構造:

var a = java.lang.reflect.Array.newInstance(java.lang.String, 5);                        

對于大部分情況,可以使用 JavaScript 的數組。將一個(gè) JavaScript 的數組作為參數傳遞給一個(gè) Java 方法時(shí) Rhino 會(huì )做自動(dòng)轉換,將其轉換為 Java 數組。

實(shí)現一個(gè) Java 接口

除了上面提到的 Invocable 接口的 getInterface 方法外,我們也可以在腳本中用如下方式:

//Define a JavaScript Object which has corresponding method                        obj={max:function(a,b){return (a > b) ?a:b;}};                        //Pass this object to an Interface                        maxImpl=com.tony.MaxMin(obj);                        //Invocation                        print (maxImpl.max(1,2));                        

如果接口只有一個(gè)方法需要實(shí)現,那么在 JavaScript 中你可以傳遞一個(gè)函數作為參數:

function func(){                        println("Hello World");                        }                        t=java.lang.Thread(func);                        t.start();                        

對于 JavaBean 的支持

Rhino 對于 JavaBean 的 get 和 is 方法將會(huì )自動(dòng)匹配,例如調用 hello.string,如果不存在 string 這個(gè)變量,Rhino 將會(huì )自動(dòng)匹配這個(gè)實(shí)例的 isString 方法然后再去匹配 getString 方法,若這兩個(gè)方法均不存在才會(huì )返回 undefined。





回頁(yè)首


命令行工具 jrunscript

在 Mustang 的發(fā)行版本中還將包含一個(gè)腳本語(yǔ)言的的命令行工具,它能夠解釋所有當前 JDK 支持的腳本語(yǔ)言。同時(shí)它也是一個(gè)用來(lái)學(xué)習腳本語(yǔ)言很好的工具。你可以在http://java.sun.com/javase/6/docs/technotes/tools/share/jrunscript.html找到這一工具的詳細介紹。





回頁(yè)首


結束語(yǔ)

腳本語(yǔ)言犧牲執行速度換來(lái)更高的生產(chǎn)率和靈活性。隨著(zhù)計算機性能的不斷提高,硬件價(jià)格不斷下降,可以預見(jiàn)的,腳本語(yǔ)言將獲得更廣泛的應用。在 JavaSE 的下一個(gè)版本中加入了對腳本語(yǔ)言的支持,無(wú)疑將使 Java 程序變得更加靈活,也會(huì )使 Java 程序員的工作更加有效率。



參考資料



關(guān)于作者

%2

吳玥顥,目前就職于 IBM 中國開(kāi)發(fā)中心 Harmony 開(kāi)發(fā)團隊。 除了對 Java 和腳本語(yǔ)言的熱愛(ài)之外,他的興趣還包括哲學(xué)、神話(huà)、歷史與籃球。此外他還是個(gè)電腦游戲高手。您可以通過(guò)wuyuehao@cn.ibm.com聯(lián)系到他。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Java SE 6之腳本引擎 讓程序如虎添翼
Java SE 6 新特性: 對腳本語(yǔ)言的支持
在Java中使用腳本語(yǔ)言 javax.script探秘
Java中運行動(dòng)態(tài)腳本 如Groovy
Java 8新特性探究(十二)Nashorn :新犀牛
java6 腳本引擎(一)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久