diff options
Diffstat (limited to 'files/zh-cn/mozilla/projects/rhino/scripting_java/index.html')
| -rw-r--r-- | files/zh-cn/mozilla/projects/rhino/scripting_java/index.html | 397 |
1 files changed, 0 insertions, 397 deletions
diff --git a/files/zh-cn/mozilla/projects/rhino/scripting_java/index.html b/files/zh-cn/mozilla/projects/rhino/scripting_java/index.html deleted file mode 100644 index 013ad3aa89..0000000000 --- a/files/zh-cn/mozilla/projects/rhino/scripting_java/index.html +++ /dev/null @@ -1,397 +0,0 @@ ---- -title: Scripting Java -slug: Mozilla/Projects/Rhino/Scripting_Java -translation_of: Mozilla/Projects/Rhino/Scripting_Java ---- -<p>这篇文章描述了如何在rhino中使用java。使用脚本调用Java有很多用途,它使得我们可以利用Java中现有的库,来帮助我们构建强大的脚本。我们可以通过编写脚本,来对Java程序进行测试。可以通过脚本来进行探索式编程,辅助Java的开发,所谓探索式编程,就是通过快速地编程调用库或API来探索这些库或API可以做什么,显而易见,脚本语言很适合探索式编程。</p> - -<p>这里注意,ECMA标准并没有包含和Java(或者其他任何对象系统)交互的标准。本文所描述的所有内容,应该被认为是一个扩展。</p> - - - -<h3 id="访问_Java_Packages_和_Classes">访问 Java Packages 和 Classes</h3> - -<p>Java的每段代码都是类的一部分,每一个JAVA类都是包的一部分。在Javascript中,脚本不属于任何package。我们可以访问Java包中的类么?</p> - -<p>Rhino定义了一个顶层的变量Packages。Packages的所有属性都是Java中顶层的包,比如java和com。比如我们可以访问java包:</p> - -<pre class="code">js> Packages.java -[JavaPackage java]</pre> - -<p>还有一种更方便的方式,Rhino定义了一个顶层的变量java,等价于Packages.java。所以上面的例子可以更简介地写成:</p> - -<pre class="code">js> java -[JavaPackage java] -</pre> - -<p>我们可以通过访问包的下层,来直接访问java类:</p> - -<pre class="code">js> java.io.File -[JavaClass java.io.File] -</pre> - -<p>如果你的脚本需要访问很多的Java类,每次都附带完整的包名会使得编程很麻烦。Rhino提供了一个顶层的方法importPackage,它的功能和Java的import一样。比如,我们可以导入java.io包中的所有类,然后直接通过类名File来访问java.io.File:</p> - -<pre class="code">js> importPackage(java.io) -js> File -[JavaClass java.io.File] -</pre> - -<p>这里importPackage(java.io)使得java.io包中的所有类(例如File)可以在顶层被访问。这和Java中的java.io.*;等价。</p> - -<p>要注意Java会暗中导入java.lang.*,但是Rhino不会。因为JavaScript的顶层对象Boolean、Math、Number、Object和String和java.lang包中同名的类并不相同。因为这种冲突,建议不要用importPackage来导入java.lang包。</p> - -<p>有一点要注意的,就是Rhino对于指定包名或类名时是如何处理错误的。如果java.Myclass是可访问的,Rhino会试图加载名为java.MyClass的类,如果加载失败,它会假设java.MyClass是一个包名,不会报错:</p> - -<pre class="code">js> java.MyClass -[JavaPackage java.MyClass] -</pre> - -<p>只有在你试图将这个对象当作类使用时,才会报错。</p> - -<h4 id="额外的包和类">额外的包和类</h4> - -<p>额外的包和类也可以在Rhino中使用。确认你的.jar或.class文件在你的classpath里,你就可以在你的JavaScript应用中导入它们。这些包基本不会在java包中,所以你在使用时,需要在包前加上前缀"Packages"。 比如你想导入 <code>org.mozilla.javascript</code> 包,你应该像下面这样去使用importPackage():</p> - -<pre class="code">$ java org.mozilla.javascript.tools.shell.Main -js> importPackage(Packages.org.mozilla.javascript); -js> Context.currentContext; -org.mozilla.javascript.Context@bb6ab6 -</pre> - -<p>偶尔,我们也会见到在一些例子中使用包的完整名称,而没有使用importPackage()。这也是可以的,只是会让你多打一些字。如果使用完整的名称,上面的例子就会变成下面这样:</p> - -<pre class="code">$ java org.mozilla.javascript.tools.shell.Main -js> jsPackage = Packages.org.mozilla.javascript; -[JavaPackage org.mozilla.javascript] -js> jsPackage.Context.currentContext; -org.mozilla.javascript.Context@bb6ab6 -</pre> - -<p>同样,你可以通过importClass()来导入一个类,上面的例子也可以像这样写:</p> - -<pre>$ java org.mozilla.javascript.tools.shell.Main -js> importClass(Packages.org.mozilla.javascript.Context); -js> Context.currentContext; -org.mozilla.javascript.Context@bb6ab6</pre> - -<h3 id="和Java一起工作">和Java一起工作</h3> - -<p>现在我们可以访问Java类,下一步就是要创建一个对象。方法就和在Java中一样, 用new来创建对象:</p> - -<pre class="code">js> new java.util.Date() -Thu Jan 24 16:18:17 EST 2002 -</pre> - -<p>如果我们将创建的对象存放在JavaScript变量中,我们可以调用它的方法:</p> - -<pre class="code">js> f = new java.io.File("test.txt") -test.txt -js> f.exists() -true -js> f.getName() -test.txt -</pre> - -<p>静态方法和属性可以直接通过类对象来访问:</p> - -<pre class="code">js> java.lang.Math.PI -3.141592653589793 -js> java.lang.Math.cos(0) -1 -</pre> - -<p>不像Java,在JavaScript里,方法就是一个对象。它可以被评估,也可以被调用。如果我们去查看这个方法,我们可以看到这个方法所有重载的形式:</p> - -<pre class="code">js> f.listFiles -function listFiles() {/* -java.io.File[] listFiles() -java.io.File[] listFiles(java.io.FilenameFilter) -java.io.File[] listFiles(java.io.FileFilter) -*/} -</pre> - -<p>输出告诉我们,File类有listFiles方法的三种重载:一种不包含参数的,另一种包含一个FilenameFilter类型的参数,第三个包含一个FileFilter类型的参数。所有的方法都返回一个File对象数组。可以观察到Java方法的参数和返回类型在探索式编程中是非常有用的,尤其是在对一个方法的参数和返回对象不确定的时候。</p> - -<p>另一个有助于探索式编程的特性,是可以看到对象中定义的所有方法和属性。用JavaScript的<code>for..in</code> , 我们可以打印这些值:</p> - -<pre class="code">js> for (i in f) { print(i) } -exists -parentFile -mkdir -toString -wait -<em>[44 others]</em> -</pre> - -<p>注意这里不仅列出了File类中的所有方法,也列出了从基类java.lang.Object中继承的方法,例如wait。这使得我们可以更好地处理那些有复杂继承关系的对象,因为我们可以看到对象中所有可用的方法。</p> - -<p>Rhino可以通过属性名来方便地访问JavaBean的属性。一个JavaBean的属性foo被方法getFoo和setFoo定义,另外,一个也叫foo的boolean类型的属性,可以被isFoo来定义。比如, 下面的代码实际上调用了File对象的getName和isDirectory方法。</p> - -<pre class="code">js> f.name -test.txt -js> f.directory -false -</pre> - -<h3 id="调用重载方法"><font><font>调用重载方法</font></font></h3> - -<p> <span id="noHighlight_0.8335956993210636">根据参数类型选择调用方法的过程称为重载决议。在 Java 中, 重载决议在编译时执行, 而在rhino中则在运行时发生。这种差异是不可避免的, 因为 JavaScript 使用动态类型, 在2章中: 由于变量的类型直到运行时才知道, 才会发生重载决议。</span></p> - -<div class="textArea" id="destText" style="direction: ltr;"> -<div><span id="noHighlight_0.37802431709856343">例如, 请查看下面的 Java 类, 它定义了许多重载方法并调用它们。</span> </div> -</div> - - - -<pre class="code">public class Overload { - - public String f(Object o) { return "f(Object)"; } - public String f(String s) { return "f(String)"; } - public String f(int i) { return "f(int)"; } - - public String g(String s, int i) { return "g(String,int)"; } - public String g(int i, String s) { return "g(int,String)"; } - - public static void main(String[] args) { - Overload o = new Overload(); - Object[] a = new Object[] { new Integer(3), "hi", Overload.class }; - for (int i = 0; i != a.length; ++i) - System.out.println(o.f(a[i])); - } -} -</pre> - -<p><span id="noHighlight_0.42609135329922554">当我们编译和执行程序, 它产生输出</span></p> - -<pre class="code">f(Object) -f(Object) -f(Object) -</pre> - -<p><span id="noHighlight_0.9335258512242988">但是, 如果我们编写一个类似的脚本</span></p> - -<pre class="code">var o = new Packages.Overload(); -var a = [ 3, "hi", Packages.Overload ]; -for (var i = 0; i != a.length; ++i) - print(o.f(a[i])); -</pre> - -<p>并且运行它,将会输出</p> - -<pre class="code">f(int) -f(String) -f(Object) -</pre> - -<p>因为Rhino在运行时选择重载方法, <span id="noHighlight_0.45030814581159195">所以它会调用与该参数匹配的更具体的类型。</span> <span id="noHighlight_0.45030814581159195">同时, 在编译时, Java 只在参数的类型上选择重载方法。</span></p> - -<p>尽管这有利于选择一种方法,这种方法可能是每个调用的更好匹配,但它确实对性能有影响,因为每次调用时都要做更多的工作。事实上,这种性能代价在实际应用中并不明显。</p> - -<p>因为重载决议发生在运行时,它可能在运行时失败。例如,如果我们用两个整数调用重载方法g,我们就会得到一个错误,因为方法的两个形式都比另一个更接近参数类型:</p> - -<pre class="code">js> o.g(3,4) -js:"<stdin>", line 2: The choice of Java method Overload.g -matching JavaScript argument types (number,number) is ambiguous; -candidate methods are: -class java.lang.String g(java.lang.String,int) -class java.lang.String g(int,java.lang.String) -</pre> - -<p><a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html</a> 提供了<font><font>一个更精确的重载语义定义</font><font>。</font></font></p> - -<h3 id="实现Java接口"> 实现Java接口</h3> - - - -<p>现在我们可以访问Java类,创建Java对象,并访问这些对象的字段、方法和属性,我们就可以轻松掌握大量的功能。但是,在少数情况下是不够用的:Java中的很多API通过提供客户端必须实现的接口来工作。其中一个例子就是<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.498039);">Thread</span></font>类:其构造函数Runnable包含一个<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.498039);">run</span></font>方法,这个方法在新线程启动时被调用。</p> - -<p>为了满足这种需求,Rhino提供了创建新的Java对象实现的接口的能力。首先,我们必须定义一个JavaScript对象,其中的函数属性的名称与Java接口所需的方法名称相匹配。要实现一个<code>Runnable</code> ,我们只需要定义一个不带参数的<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.498039);">run</span></font>单方法。如果你还记得第3章,可以用{ propertyName: value}符号定义一个JavaScript对象。我们可以在这里结合函数表达式使用这个语法来用一个 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.498039);">run</span></font>方法定义一个JavaScript对象:</p> - -<pre class="code">js> obj = { run: function () { print("\nrunning"); } } -[object Object] -js> obj.run() - -running -</pre> - - - -<p>现在我们可以通过构建一个 <code>Runnable</code> 来实现 <code>Runnable</code> 接口的对象:</p> - -<pre><code>js> r = new java.lang.Runnable(obj);</code></pre> - - - -<pre class="code">js> r = new java.lang.Runnable(obj); -[object JavaObject] -</pre> - -<p><font><font>在Java中,不可能</font></font><font><font>在接口上</font><font>使用</font></font><code>new</code><font><font>运算符,因为没有可用的实现。</font><font>Rhino从JavaScript对象中获取实现</font></font><code>obj</code><font><font>。</font><font>现在我们有一个对象实现</font></font><code>Runnable</code><font><font>,我们可以创建</font></font><code>Thread</code><font><font>并运行它。</font><font>我们定义的函数</font></font><code>run </code><font><font>将在新线程上调用。</font></font></p> - -<pre class="code">js> t = new java.lang.Thread(r) -Thread[Thread-2,5,main] -js> t.start() -js> - -running -</pre> - - - -<p><font><font>最终</font></font><code>js</code><font><font>提示和新线程的输出可能以任意顺序显示,具体取决于线程调度。</font></font></p> - -<p>在后台,Rhino为一个新的Java类生成字节码,该类实现 <code>Runnable</code> 并转发对其 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.498039);">run</span></font>方法的所有调用,并转发给关联的JavaScript对象。实现此类的对象称为Java适配器。因为转发到JavaScript是在运行时发生的,所以可能会延迟定义实现接口的方法直到它们被调用。虽然省略必要的方法对大编程来说是一种糟糕的做法,但它对小脚本和探索性编程很有用。</p> - - - -<h3 id="JavaAdapter构造函数"><font><font>JavaAdapter构造函数</font></font></h3> - -<p>在前面的章节中,我们使用 <code>new</code> 运算符与Java接口创建Java适配器。这种方法有其局限性:不可能实现多个接口,也不能扩展非抽象类。因为这些<font><font>原因,有一个</font></font> <code>JavaAdapter</code> 构造函数。</p> - -<p><code>JavaAdapter</code><font><font>构造函数</font><font>的语法</font><font>是:</font></font></p> - -<pre class="code">new JavaAdapter(javaIntfOrClass, [javaIntf, ..., javaIntf,] javascriptObject) -</pre> - -<p><code><font face="Open Sans, arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">这里</span></font>javaIntfOrClass</code>是一个实现的接口或一个扩展的类,并且<code>javaIntf</code>是实现接口的接口。而<code>javascriptObject</code> 则包含从Java适配器调用的方法的JavaScript对象。</p> - -<p>在实践中,几乎不需要<code>JavaAdapter</code> 直接调用构造函数。大多数情况下,使用<code>new</code>运算符之前的语法就足够了。</p> - -<h3 id="作为Java接口的JavaScript函数"><font><font>作为Java接口的JavaScript函数</font></font></h3> - -<p>通常我们只需要使用一种方法实现一个接口,<font><font>就像前面的</font></font> <code>Runnable</code> <font><font>例子或者提供各种事件监听器实现一样。</font>为了方便这个,Rhino允许在这种接口传递JavaScript函数。</font><font>该函数被称为接口方法的实现。</font></p> - -<p>这里是简化的 <code>Runnable</code> 实例:</p> - -<pre class="code">js> t = java.lang.Thread(function () { print("\nrunning"); }); -Thread[Thread-0,5,main] -js> t.start() -js> -running -</pre> - -<p><font>如果所有的方法都具有相同的签名,Rhino还允许使用JavaScript函数作为Java接口的实现方法。当调用函数时,Rhino将方法的名称作为附加参数传递。函数可以使用它</font>来代表被调用<font>的方法:</font></p> - -<pre class="code">js> var frame = new Packages.javax.swing.JFrame(); -js> frame.addWindowListener(function(event, methodName) { - if (methodName == "windowClosing") { - print("Calling System.exit()..."); java.lang.System.exit(0); - } - }); -js> frame.setSize(100, 100); -js> frame.visible = true; -true -js> Calling System.exit()... -</pre> - -<h3 id="创建Java数组"><font><font>创建Java数组</font></font></h3> - -<p>Rhino不提供创建Java数组的特殊语法。你必须使用这个 <code>java.lang.reflect.Array</code> 类来达到这个目的。要创建一个由五个Java字符串组成的数组,可以进行以下调用:</p> - -<pre class="code">js> a = java.lang.reflect.Array.newInstance(java.lang.String, 5); -[Ljava.lang.String;@7ffe01 -</pre> - -<p>要创建一个基本类型数组,我们必须使用 <code>java.lang</code> 包中相关对象类中定义的特殊TYPE字段。例如,要创建一个字节数组,我们必须使用特殊字段 <code>java.lang.Byte.TYPE</code>:</p> - -<pre class="code">js> a = java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 2); -[C@7a84e4 -</pre> - -<p>而且结果值是允许被使用在该类型的Java数组的任何地方。</p> - -<pre class="code">js> a[0] = 104 -104 -js> a[1] = 105 -105 -js> new java.lang.String(a) -hi -</pre> - -<h3 id="Java字符串和JavaScript字符串"><font><font>Java字符串和JavaScript字符串</font></font></h3> - -<p><font><font>请记住,Java字符串和JavaScript字符串是</font></font><strong><font><font>不</font></font></strong><font><font>一样的。</font><font>Java字符串类型的实例,</font></font><code>java.lang.String</code> ,<font><font>并具有由该类定义的所有方法。</font><font>JavaScript字符串具有由...定义的方法,</font></font><code>String.prototype</code>. <font><font>最常见的绊脚石是</font></font> <code>length</code>, <font><font><font face="consolas, Liberation Mono, courier, monospace">这是Java</font>字符串方法和JavaScript字符串的动态属性:</font></font></p> - -<pre class="code">js> javaString = new java.lang.String("Java") -Java -js> jsString = "JavaScript" -JavaScript -js> javaString.length() -4 -js> jsString.length -10 -</pre> - -<p>Rhino <font>在减少这两种类型之间的差异方面提供了一些帮助。</font><font>首先,您可以将JavaScript字符串传递给需要Java字符串的Java方法,Rhino将执行转换。</font><font>实际上,我们在前面</font><code>java.lang.String</code> <font>例子中</font><font>的</font><font>构造函数</font><font>调用中看到了这个特性</font><font>。</font></p> - -<p><font>如果</font><code>java.lang.String</code> <font>类尚未定义它们,Rhino还会使JavaScript方法可用于Java字符串。例如:</font></p> - -<pre class="code">js> javaString.match(/a.*/) -ava -</pre> - -<h3 id="JavaImporter_构造函数">JavaImporter 构造函数</h3> - -<p><code>JavaImporter</code>是一个新的全局构造函数,它允许在脚本化Java时省略显式的包名称:</p> - -<pre>var SwingGui = JavaImporter(Packages.javax.swing, - Packages.javax.swing.event, - Packages.javax.swing.border, - java.awt.event, - java.awt.Point, - java.awt.Rectangle, - java.awt.Dimension); -... - -with (SwingGui) { - var mybutton = new JButton(test); - var mypoint = new Point(10, 10); - var myframe = new JFrame(); -... -} -</pre> - -<p>以前,这样的功能仅适用于将 <code>org.mozilla.javascript.ImporterTopLevel</code> 用作顶级作用域的嵌入。这个类提供额外的 <code>importPackage()</code> 和<code>importClass()</code> 全局函数的脚本,但其广泛的使用有污染Java类名的全局命名空间的趋势,还有防止垃圾收集加载类。</p> - -<p>详情请参阅 <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=245882">Bugzilla 245882</a>.</p> - -<h3 id="Java_异常">Java 异常</h3> - - - -<p><font><font>JavaScript代码使用</font></font> <code>try ... catch</code> 语句<font><font>可以捕获Java方法抛出的异常</font><font>。</font><font>Rhino将Java异常封装到具有以下属性的错误对象中:</font></font></p> - -<ul> - <li><code>javaException</code><font><font>:Java方法抛出的原始异常</font></font></li> - <li><code>rhinoException</code><font><font>:由Rhino运行时包装的异常</font></font></li> -</ul> - -<p><code>instanceof</code><font><font>运算符可用于查询异常的类型:</font></font></p> - - - -<pre>try { - java.lang.Class.forName("NonExistingClass"); -} catch (e) { - if (e.javaException instanceof java.lang.ClassNotFoundException) { - print("Class not found"); - } -} -</pre> - -<p>Rhino 还支持对 <code>try... catch</code> 语句的扩展,允许定义条件捕获异常:</p> - -<pre>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"); - } -} - -classForName("NonExistingClass"); -classForName(null); -</pre> |
