现在Java SE
已经发布
在明年Java SE
也将发布
Java SE
较Java SE
有了很大的改进
它的功能更强
而且是专为Vista所设计
这就意味着Java SE
将是Vista上的最佳选择
而Java SE
所提供的最众多的新功能必将成为它的最大卖点
Java SE 最引人注目的新功能之一就是内嵌了脚本支持在默认情况下Java SE 只支持JavaScript但这并不以为着Java SE 只能支持JavaScript在Java SE 中提供了一些接口来定义一个脚本规范也就是JSR通过实现这些接口Java SE 可以支持任意的脚本语言(如PHP或Ruby)
运行第一个脚本程序
在使用Java SE 运行脚本之前必须要知道你的Java SE 支持什么脚本语言在javaxscript包中有很多的类但这些类中最主要的是ScriptEngineManager可以通过这个类得到当前Java SE 所支持的所有脚本如下面例子将列出所有可以使用的脚本引擎工厂
import javaxscript*;
import javaio*;
import javautil*;
import static javalangSystem*;
public class ListScriptEngines
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 得到所有的脚本引擎工厂
List<ScriptEngineFactory> factories = managergetEngineFactories();
// 这是Java SE 和Java SE 的新For语句语法
for (ScriptEngineFactory factory: factories)
{
// 打印脚本信息
outprintf(Name: %s%n +
Version: %s%n +
Language name: %s%n +
Language version: %s%n +
Extensions: %s%n +
Mime types: %s%n +
Names: %s%n
factorygetEngineName()
factorygetEngineVersion()
factorygetLanguageName()
factorygetLanguageVersion()
factorygetExtensions()
factorygetMimeTypes()
factorygetNames());
// 得到当前的脚本引擎
ScriptEngine engine = factorygetScriptEngine();
}
}
}
上面的例子必须要在Java SE 中编译其中import static javalangSystem*是新的语法将System中的所有静态成员进行引用以后就可以直接使用outin或err了
通过运行java ListScriptEngines将显示如下信息
Name: Mozilla Rhino
Version: release
Language name: ECMAScript
Language version:
Extensions: [js]
Mime types: [application/javascript application/ecmascript text/javascript text/ecmascript]
Names: [js rhino JavaScript javascript ECMAScript ecmascript]
在最下面一行是脚本的别名也就是使用它们中的任意一个都可以得到一个具体的脚本引擎有种方法
·根据扩展名得到脚本引擎
ScriptEngine engine = managergetEngineByExtension(js);
getEngineByExtension的参数就是Extensions:[js]中[…]里的部分
·根据Mime类型得到脚本引擎
ScriptEngine engine = managergetEngineByMimeType(text/javascript);
getEngineByMimeType的参数可以是Mime types: [application/javascript application/ecmascript text/javascript text/ecmascript]中的任何一个可以将text/javascript改成text/ecmascript
·根据名称得到脚本引擎
ScriptEngine engine = managergetEngineByName(javascript);
getEngineByName后的参数可以是Names: [js rhino JavaScript javascript ECMAScript ecmascript]中的任何一个如可以将javascript改成ecmascript
上面已经讨论了执行脚本的第一步就是得到一个可用的脚本引擎在完成这项工作之 后就可以利用这个脚本引擎执行相应的脚本了我们可以使用ScriptEngine的eval方法来执行脚本eval方法被重载的多次但最常用的是public Object eval(String script)
下面的例子演示了如何使用eval方法来执行javascript脚本
import javaxscript*;
import javaio*;
import static javalangSystem*;
public class FirstJavaScript
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 得到javascript脚本引擎
ScriptEngine engine = managergetEngineByName(javascript);
try
{
// 开始运行脚本并返回当前的小时
Double hour = (Double)engineeval(var date = new Date(); +dategetHours(););
String msg;
// 将小时转换为问候信息
if (hour < )
{
msg = 上午好;
}
else if (hour < )
{
msg = 下午好;
}
else if (hour < )
{
msg = 晚上好;
}
else
{
msg = 晚安;
}
outprintf(小时 %s: %s%n hour msg);
}
catch (ScriptException e)
{
errprintln(e);
}
}
}
上面的例子通过得到当前的小时并将其转化为问候语上面的程序的输出信息为
小时上午好
这个例子最值得注意的是执行的句脚本最后一句是dategetHours()并未将这个值赋给一个javascript变量这时eval方法就将这样的值返回这有些类似C语言的(…)运算符如(c=a+b c + d)这个表达式的返回值是a+b+d
和脚本语言进行交互
上面例子只是运行了一个非常简单的脚本这个脚本是孤立的并未通过Java向这脚本传递任何的值虽然从这个脚本返回了一个值但这种返回方式是隐式的
脚本引擎除了这些简单的功能还为我们提供了更强大的功能甚至可以通过Java向脚本语言中传递参数还可以将脚本语言中的变量的值取出来这些功能要依靠ScriptEngine中的两个方法put和get
put 有两个参数一个是脚本变量名另一个是变量的值这个值是Object类型因此可以传递任何值
get 有一个参数就是脚本变量的名
下面的代码通过javascript脚本将一个字符串翻转(这个字符串是通过java传给javascript的)然后通过java得到这个被翻转后的字符后然后输出
import javaxscript*;
import javaio*;
import static javalangSystem*;
public class ReverseString
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 建立javascript脚本引擎
ScriptEngine engine = managergetEngineByName(javascript);
try
{
// 将变量name和变量值abcdefg传给javascript脚本
engineput(name abcdefg);
// 开始执行脚本
engineeval(var output = ; +
for (i = ; i <= namelength; i++) { +
output = namecharAt(i) + output +
});
// 得到output变量的值
String name = (String)engineget(output);
outprintf(被翻转后的字符串%s name);
}
catch (ScriptException e)
{
errprintln(e);
}
}
}
以上代码的输出结果为被翻转后的字符串gfedcba
让脚本运行得更快
众所周知解释运行方式是最慢的运行方式上述的几个例子无一例外地都是以解释方式运行的由于Java EE 的脚本引擎可以支持任何实现脚本引擎接口的语言有很多这样的语言提供了编译功能也就是说在运行脚本之前要先将这些脚本进行编译(这里的编译一般将不是生成可执行文件而只是在内存中编译成更容易运行的方式)然后再执行如果某段脚本要运行之交多次的话使用这种方式是非常快的我们可以使用ScriptEngine的compile方法进行编译并不是所有脚本引擎都支持编译只有实现了Compilable接口的脚本引擎才可以使用compile进行编译否则将抛出一个错误下面的例子将演示如何使用compile方法编译并运行javascript脚本
import javaxscript*;
import javaio*;
import static javalangSystem*;
public class CompileScript
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = managergetEngineByName(javascript);
engineput(counter ); // 向javascript传递一个参数
// 判断这个脚本引擎是否支持编译功能
if (engine instanceof Compilable)
{
Compilable compEngine = (Compilable)engine;
try
{
// 进行编译
CompiledScript script = pile(function count() { +
counter = counter +; +
return counter; +
}; count(););
outprintf(Counter: %s%n scripteval());
outprintf(Counter: %s%n scripteval());
outprintf(Counter: %s%n scripteval());
}
catch (ScriptException e)
{
errprintln(e);
}
}
else
{
errprintln(这个脚本引擎不支持编译!);
}
}
}
上面的代码运行后的显示信息如下
Counter:
Counter:
Counter:
在这个例子中先通过compile方法将脚本编译然后通过eval方法多次进行调用在这段代码中只有一个函数因此eval就返回了这个函数的值
动态调用脚本语言的方法上面的例子只有一个函数可以通过eval进行调用并将它的值返回但如果脚本中有多个函数或想通过用户的输入来决定调用哪个函数这就需要使用invoke方法进行动态调用和编译一样脚本引擎必须实现Invocable接口才可以动态调用脚本语言中的方法下面的例子将演示如何通过动态调用的方式来运行上面的翻转字符串的javascript脚本
import javaxscript*;
import javaio*;
import static javalangSystem*;
public class InvocableTest
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = managergetEngineByName(javascript);
if (engine instanceof Invocable)
{
try
{
engineeval(function reverse(name) { +
var output = ; +
for (i = ; i <= namelength; i++) { +
output = namecharAt(i) + output +
} return output;});
Invocable invokeEngine = (Invocable)engine;
Object o = invokeEngineinvoke(reverse name);
outprintf(翻转后的字符串%s name);
}
catch (NoSuchMethodException e)
{
errprintln(e);
}
catch (ScriptException e)
{
errprintln(e);
}
}
else
{
errprintln(这个脚本引擎不支持动态调用);
}
}
动态实现接口
脚本引擎还有一个更吸引的功能那就是动态实现接口如我们要想让脚本异步地执行即通过多线程来执行那InvokeEngine类必须实现Runnable接口才可以通过Thread启动多线程因此可以通过getInterface方法来使InvokeEngine动态地实现Runnable接口这样一般可分为步进行
使用javascript编写一个run函数
engineeval(function run() {print(异步执行);});
通过getInterface方法实现Runnable接口
Runnable runner = invokeEnginegetInterface(Runnableclass);
使用Thread类启动多线程
Thread t = new Thread(runner);
tstart();
下面是实现这个功能的详细代码
import javaxscript*;
import static javalangSystem*;
public class InterfaceTest
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = managergetEngineByName(javascript);
try
{
engineeval(function run() {print(异步调用);});
Invocable invokeEngine = (Invocable)engine;
Runnable runner = invokeEnginegetInterface(Runnableclass);
Thread t = new Thread(runner);
tstart();
tjoin();
}
catch (InterruptedException e)
{
errprintln(e);
}
catch (ScriptException e)
{
Systemerrprintln(e);
}
}
}
其实上面的代码是通过javascript实现了Runnable接口的run方法