前言

本文中所有的代码使用 JavaScript 编写,但你也可以用其他兼容 JSR 223 的脚本语言。这些例子可作为脚本文件也可以在交互式 Shell 中一次运行一个语句的方式来运行。在 JavaScript 中访问对象的属性和方法的语法与 Java 语言相同。

本文包含如下几部分:

1、访问 Java 类

为了在 JavaScript 中访问原生类型或者引用 Java 类型,可以调用 Java.type() 函数,该函数根据传入的完整类名返回对应对象的类型。下面代码显示如何获取不同的对象类型:

?
1
2
3
4
var ArrayList = Java.type("java.util.ArrayList");
var intType = Java.type("int");
var StringArrayType = Java.type("java.lang.String[]");
var int2DArrayType = Java.type("int[][]");

在 JavaScript 中使用 Java.type() 函数返回的类型对象的方法跟在 Java 的类似。

例如你可以使用如下方法来实例化一个类:

?
1
var anArrayList = new Java.type("java.util.ArrayList");

Java 类型对象可用来实例化 Java 对象。下面的代码显示如何使用默认的构造函数实例化一个新对象以及调用包含参数的构造函数:

?
1
2
3
var ArrayList = Java.type("java.util.ArrayList");
var defaultSizeArrayList = new ArrayList;
var customSizeArrayList = new ArrayList(16);

你可以使用 Java.type() 方法来获取对象类型,可以使用如下方法来访问静态属性以及方法:

?
1
2
var File = Java.type("java.io.File");
File.createTempFile("nashorn", ".tmp");

如果要访问内部静态类,可以传递美元符号 $ 给 Java.type() 方法。

下面代码显示如何返回 java.awt.geom.Arc2DFloat 内部类:

?
1
var Float = Java.type("java.awt.geom.Arc2D$Float");

如果你已经有一个外部类类型对象,那么你可以像访问属性一样访问其内部类,如下所示:

?
1
2
var Arc2D = Java.type("java.awt.geom.Arc2D")
var Float = Arc2D.Float

由于是非静态内部类,必须传递的是外部类实例作为参数给构造函数。

虽然在 JavaScript 中使用类型对象跟在 Java 中类似,但其与 java.lang.Class 对象还是有些区别的,这个区别就是 getClass() 方法的返回值。你可以使用 classstatic 属性来获取这个信息。

下面代码显示二者的区别:

?
1
2
3
4
5
6
7
8
9
var ArrayList = Java.type("java.util.ArrayList");
var a = new ArrayList;
// All of the following are true:
print("Type acts as target of instanceof: " + (a instanceof ArrayList));
print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()));
print("Type is not the same as instance's getClass(): " + (a.getClass() !== ArrayList));
print("Type's `class` property is the same as instance's getClass(): " + (a.getClass() === ArrayList.class));
print("Type is the same as the `static` property of the instance's getClass(): " + (a.getClass().static === ArrayList));

在语法和语义上,JavaScript 在编译时类表达式和运行时对象都和 Java 语义类似。不过在 Java 中 Class 对象是没有名为 static 这样的属性,因为编译时的类表达式不作为对象。

2、导入 Java 包和类

为了根据其简单的名称来访问 Java 类,我们可以使用 importPackage()importClass() 函数来导入 Java 的包和类。这些函数存在于兼容性脚本文件 (mozilla_compat.js) 中。

下面例子展示如何使用 importPackage()importClass() 函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
// Load compatibility script
load("nashorn:mozilla_compat.js");
// Import the java.awt package
importPackage(java.awt);
// Import the java.awt.Frame class
importClass(java.awt.Frame);
// Create a new Frame object
var frame = new java.awt.Frame("hello");
// Call the setVisible() method
frame.setVisible(true);
// Access a JavaBean property
print(frame.title);

可以通过 Packages 全局变量来访问 Java 包,例如Packages.java.util.Vector 或者 Packages.javax.swing.JFrame。但标准的 Java SE 包有更简单的访问方式,如: java 对应 Packages.java, javax 对应 Packages.javax, 以及 org 对应 Packages.org。

java.lang 包默认不需要导入,因为这会和 ObjectBooleanMath 等其他 JavaScript 内建的对象在命名上冲突。此外,导入任何 Java 包和类也可能导致 JavaScript 全局作用域下的变量名冲突。为了避免冲突,我们定义了一个 JavaImporter 对象,并通过 with 语句来限制导入的 Java 包和类的作用域,如下列代码所示:

?
1
2
3
4
5
6
7
8
9
// Create a JavaImporter object with specified packages and classes to import
var Gui = new JavaImporter(java.awt, javax.swing);
// Pass the JavaImporter object to the "with" statement and access the classes
// from the imported packages by their simple names within the statement's body
with (Gui) {
 var awtframe = new Frame("AWT Frame");
 var jframe = new JFrame("Swing JFrame");
};

3、使用 Java 数组

为了创建 Java 数组对象,首先需要获取 Java 数组类型对象并进行初始化。JavaScript 访问数组元素的语法以及 length 属性都跟 Java 一样,如下列代码所示:

?
1
2
3
4
5
6
7
8
9
var StringArray = Java.type("java.lang.String[]");
var a = new StringArray(5);
// Set the value of the first element
a[0] = "Scripting is great!";
// Print the length of the array
print(a.length);
// Print the value of the first element
print(a[0]);

给定一个 JavaScript 数组 我们还可以用 Java.to() 方法将它转成 Java 数组。我们需要将 JavaScript 数组作为参数传给该方法,并指定要返回的数组类型,可以是一个字符串,或者是类型对象。我们也可以忽略类型对象参数来返回 Object[] 数组。转换操作是根据 ECMAScript 转换规则进行的。下面代码展示如何通过不同的 Java.to() 的参数将 JavaScript 数组变成 Java 数组:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建一个 JavaScript 数组
var anArray = [1, "13", false];
// 将数组转换成 java 的 int[] 数组
var javaIntArray = Java.to(anArray, "int[]");
print(javaIntArray[0]); // prints the number 1
print(javaIntArray[1]); // prints the number 13
print(javaIntArray[2]); // prints the number 0
// 将 JavaScript 数组转换成 Java 的 String[] 数组
var javaStringArray = Java.to(anArray, Java.type("java.lang.String[]"));
print(javaStringArray[0]); // prints the string "1"
print(javaStringArray[1]); // prints the string "13"
print(javaStringArray[2]); // prints the string "false"
// 将 JavaScript 数组转换成 Java 的 Object[] 数组
var javaObjectArray = Java.to(anArray);
print(javaObjectArray[0]); // prints the number 1
print(javaObjectArray[1]); // prints the string "13"
print(javaObjectArray[2]); // prints the boolean value "false"

你可以使用 Java.from() 方法来将一个 Java 数组转成 JavaScript 数组。

下面代码演示如何将一个包含当前目录下文件列表的数组转成 JavaScript 数组:

?
1
2
3
4
5
6
7
8
// Get the Java File type object
var File = Java.type("java.io.File");
// Create a Java array of File objects
var listCurDir = new File(".").listFiles();
// Convert the Java array to a JavaScript array
var jsList = Java.from(listCurDir);
// Print the JavaScript array
print(jsList);

注意:

大多数情况下,你可以在脚本中使用 Java 对象而无需转换成 JavaScript 对象。

4、实现 Java 接口

在 JavaScript 实现 Java 接口的语法与在 Java 总定义匿名类的方法类似。我们只需要实例化接口并用 JavaScript 函数实现其方法即可。

下面代码演示如何实现 Runnable 接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
// Create an object that implements the Runnable interface by implementing
// the run() method as a JavaScript function
var r = new java.lang.Runnable() {
 run: function() {
  print("running...\n");
 }
};
// The r variable can be passed to Java methods that expect an object implementing
// the java.lang.Runnable interface
var th = new java.lang.Thread(r);
th.start();
th.join();

如果一个方法希望一个对象,这个对象实现了只有一个方法的接口,你可以传递一个脚本函数给这个方法,而不是传递对象。例如,在上面的例子中 Thread() 构造函数要求一个实现了 Runnable 接口的对象作为参数。我们可以利用自动转换的优势传递一个脚本函数给 Thread() 构造器。

下面的例子展示如何创建一个 Thread 对象而无需实现 Runnable 接口:

?
1
2
3
4
5
6
7
8
9
10
// Define a JavaScript function
function func() {
 print("I am func!");
};
// Pass the JavaScript function instead of an object that implements
// the java.lang.Runnable interface
var th = new java.lang.Thread(func);
th.start();
th.join();

你可以通过传递相关类型对象给 Java.extend() 函数来实现多个接口。

5、扩展抽象 Java 类

你可以实例化一个匿名的抽象类的子类,只需要给构造函数传递一个 JavaScript 对象,对象中包含了一些属性对应了抽象类方法实现的值。如果一个方法是重载的,JavaScript 函数将会提供所有方法变种的实现。下面例子显示如何初始化抽象类 TimerTask 的子类:

?
1
2
var TimerTask = Java.type("java.util.TimerTask");
var task = new TimerTask({ run: function() { print("Hello World!") } });

除了调用构造函数并传递参数,我们还可以在 new 表达式后面直接提供参数。

下面的例子显示该语法的使用方法(类似 Java 匿名内部类的定义),这比上面的例子要简单一些:

?
1
2
3
4
5
var task = new TimerTask {
 run: function() {
  print("Hello World!")
 }
};

如果抽象类包含单个抽象方法(SAM 类型),那么我们就无需传递 JavaScript 对象给构造函数,我们可以传递一个实现了该方法的函数接口。下面的例子显示如何使用 SAM 类型来简化代码:

?
1
var task = new TimerTask(function() { print("Hello World!") });

不管你选择哪种语法,如果你需要调用一个包含参数的构造函数,你可以在实现对象和函数中指定参数。

如果你想要调用一个要求 SAM 类型参数的 Java 方法,你可以传递一个 JavaScript 函数给该方法。Nashorn 将根据方法需要来实例化一个子类并使用这个函数去实现唯一的抽象方法。

下面的代码显示如何调用 Timer.schedule() 方法,该方法要求一个 TimerTask 对象作为参数:

?
1
2
var Timer = Java.type("java.util.Timer");
Timer.schedule(function() { print("Hello World!") });

注意:

前面的语法假设所要求的 SAM 类型是一个接口或者包含一个默认构造函数,Nashorn 用它来初始化一个子类。这里是无法使用不包含默认构造函数的类的。

6、扩展具体 Java 类

为了避免混淆,扩展抽象类的语法不能用于扩展具体类。因为一个具体类是可以被实例化的,这样的语法会被解析成试图创建一个新的类实例并传递构造函数所需类的对象(如果预期的对象类型是一个接口)。为了演示这个问题,请看看下面的示例代码:

?
1
var t = new java.lang.Thread({ run: function() { print("Thread running!") } });

这行代码被解析为扩展了 Thread 类并实现了 run() 方法,而 Thread 类的实例化是通过传递给其构造函数一个实现了 Runnable 接口的对象。

为了扩展一个具体类,传递其类型对象给 Java.extend() 函数,然后返回其子类的类型对象。紧接着就可以使用这个子类的类型对象来创建实例并提供额外的方法实现。

下面的代码将向你展示如何扩展 Thread 类并实现 run() 方法:

?
1
2
3
4
var Thread = Java.type("java.lang.Thread");
var threadExtender = Java.extend(Thread);
var t = new threadExtender() {
 run: function() { print("Thread running!") }};

Java.extend() 函数可以获取多个类型对象的列表。你可以指定不超过一个 Java 的类型对象,也可以指定跟 Java接口一样多的类型对象数量。返回的类型对象扩展了指定的类(或者是 java.lang.Object ,如果没有指定类型对象的话),这个类实现了所有的接口。类的类型对象无需在列表中排在首位。

7、访问超类(父类)的方法

想要访问父类的方法可以使用 Java .super() 函数。

下面的例子中显示如何扩展 java.lang.Exception 类,并访问父类的方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Example 3-1 访问父类的方法 (super.js)
var Exception = Java.type("java.lang.Exception");
var ExceptionAdapter = Java.extend(Exception);
var exception = new ExceptionAdapter("My Exception Message") {
 getMessage: function() {
  var _super_ = Java.super(exception);
  return _super_.getMessage().toUpperCase();
 }
}
try {
 throw exception;
} catch (ex) {
 print(exception);
}

如果你运行上面代码将会打印如下内容:

?
1
jdk.nashorn.javaadapters.java.lang.Exception: MY EXCEPTION MESSAGE

8、绑定实现到类

前面的部分我们描述了如何扩展 Java 类以及使用一个额外的 JavaScript 对象参数来实现接口。实现是绑定的具体某个实例上的,这个实例是通过 new 来创建的,而不是整个类。这样做有一些好处,例如运行时的内存占用,因为 Nashorn 可以为每个实现的类型组合创建一个单一的通用适配器。

下面的例子展示不同的实例可以是同一个 Java 类,而其 JavaScript 实现对象却是不同的:

?
1
2
3
4
5
6
var Runnable = java.lang.Runnable;
var r1 = new Runnable(function() { print("I'm runnable 1!") });
var r2 = new Runnable(function() { print("I'm runnable 2!") });
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

上述代码将打印如下结果:

?
1
2
3
I'm runnable 1!
I'm runnable 2!
We share the same class: true

如果你想传递类的实例给外部 API(如 JavaFX 框架,传递 Application 实例给 JavaFX API),你必须扩展一个 Java 类或者实现了与该类绑定的接口,而不是它的实例。你可以通过传递一个 JavaScript 对象绑定实现类并传递给 Java.extend() 函数的最后一个参数。这个会创建一个跟原有类包含一样构造函数的新类,因为它们不需要额外实现对象参数。

下面的例子展示如何绑定实现到类中,并演示在这种情况下对于不同调用的实现类是不同的:

?
1
2
3
4
5
6
var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") });
var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") });
var r1 = new RunnableImpl1();var r2 = new RunnableImpl2();
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

上面例子执行结果如下:

?
1
2
3
I'm runnable 1!
I'm runnable 2!
We share the same class: false

将实现对象从构造函数调用移到 Java.extend() 函数调用可以避免在构造函数调用中所需的额外参数。每一个 Java.extend() 函数的调用都需要一个指定类的实现对象生成一个新的 Java 适配器类。带类边界实现的适配器类仍可以使用一个额外的构造参数用来进一步重写特定实例的行为。因此你可以合并这两种方法:你可以在一个基础类中提供部分 JavaScript 实现,然后传递给 Java.extend() 函数,以及在对象中提供实例实现并传递给构造函数。对象定义的函数并传递给构造函数时将覆盖对象的一些函数定义。

下面的代码演示如何通过给构造函数传递一个函数来覆盖类边界对象的函数:

?
1
2
3
4
5
6
var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") });
var r1 = new RunnableImpl();
var r2 = new RunnableImpl(function() { print("I'm runnable 2!") });
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

上面例子执行后打印结果如下:

?
1
2
3
I'm runnable 1!
I'm runnable 2!
We share the same class: true

9、选择方法重载变体

Java 的方法可以通过使用不同的参数类型进行重载。Java 编译器 (javac) 会在编译时选择正确的方法来执行。在 Nashorn 中对Java 重载方法的解析实在方法被调用的时候执行的。也是根据参数类型来确定正确的方法。但如果实际的参数类型会导致模棱两可的情况下,我们可以显式的指定具体某个重载变体。这会提升程序执行的性能,因为 Nashorn 引擎无需在调用过程中去辨别该调用哪个方法。

重载的变种作为特别的属性暴露出来。我们可以用字符串的形式来引用它们,字符串包含方法名称、参数类型,两者使用圆括号包围起来。

下面的例子显示如何调用  System.out.println() 方法带 Object 参数的变种,我们传递一个 “hello” 字符串给它:

?
1
2
var out = java.lang.System.out;
out["println(Object)"]("hello");

上述的例子中,光使用 Object 类名就足够了,因为它是唯一标识正确的签名。你必须使用完整的类名的情况是两个重载变种函数使用不同的参数类型,但是类型的名称相同(这是可能的,例如不同包中包含相同的类名)。

10、映射数据类型

绝大多数 Java 和 JavaScript 之前的转换如你所期待的运行良好。前面的章节中我们提到过一些简单的 Java 和 JavaScript 之间的数据类型映射。例如可以显式地转换数组类型数据,JavaScript 函数可以在当成参数传递给 Java 方法时自动转换成 SAM 类型。每个 JavaScript 对象实现了 java.util.Map 接口来让 API 可以直接接受映射。当传递数值给 Java API 时,会被转成所期待的目标数值类型,可以是封装类型或者是原始数据类型。如果目标类型不太确定(如 Number),你只能要求它必须是 Number 类型,然后专门针对该类型是封装了 Double、Integer 或者是 Long 等等。内部的优化使得数值可以是任何封装类型。同事你可以传递任意 JavaScript 值给 Java API,不管是封装类型还是原始类型,因为 JavaScript 的 ToNumber 转换算法将会自动处理其值。如果 Java 方法要求一个 String 或者 Boolean 对象参数,JavaScript 将会使用 ToStringToBoolean 转换来获得其值。

注意:

因为对字符串操作的内部性能优化考虑,JavaScript 字符串并不总是对应 java.lang.String 类型,也可能是 java.lang.CharSequence 类型。如果你传递一个 JavaScript 字符串给要求 java.lang.String 参数的 Java 方法,那么这个 JavaScript 字符串就是 java.lang.String 类型,但如果你的方法签名想要更加泛型化(例如接受的参数类型是 java.lang.Object),那么你得到的参数对象就会使一个实现了 CharSequence 类的对象,而不是一个 Java 字符串对象。

转载于:https://www.cnblogs.com/wangjunxiao/p/8167458.html

java与javascript相关推荐

  1. 调查发现 Java 和 JavaScript 是企业开发的顶级语言

    开发四年只会写业务代码,分布式高并发都不会还做程序员?   在今天的多语言编程世界中,企业不再局限于一种特定的编程语言. 但是,Cloud Foundry Foundation (CFF) 近日发布的 ...

  2. java中table是什么标签_[Java教程]javascript格式化table标签内容

    [Java教程]javascript格式化table标签内容 0 2015-07-12 20:00:08 项目中遇到这样的需求,一大段文章正文的html代码在手机中显示不全,原因是由于其它有table ...

  3. java js 转换_[Java教程]javascript 类型转换。

    [Java教程]javascript 类型转换. 0 2016-10-11 18:00:06 目录: 1 : 伪对象 2 : 转换为字符串 3 : 数字转字符串 4 : 转换为数字 5 : 转换为Bo ...

  4. 【扫盲帖】Java、JavaScript、JSP、JScript 的区别与联系

    [转自]http://developer.51cto.com/art/200907/140294.htm ==>JavaScript与Java的联系和区别 JavaScript与Java的语法比 ...

  5. java with关键字_[Java教程]javascript 里面 with 关键字

    [Java教程]javascript 里面 with 关键字 0 2016-02-18 11:00:02 1.with的基本概念 with语句的作用是将代码的作用域设置到一个特定的作用域中,目的是为了 ...

  6. java引用类型和值类型_[Java教程]JavaScript中值类型和引用类型的区别

    [Java教程]JavaScript中值类型和引用类型的区别 0 2017-02-24 00:00:35 JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和 ...

  7. Nashorn——在JDK 8中融合Java与JavaScript之力--转

    原文地址:http://www.infoq.com/cn/articles/nashorn 从JDK 6开始,Java就已经捆绑了JavaScript引擎,该引擎基于Mozilla的Rhino.该特性 ...

  8. Java和JavaScript区别与联系

    Java和JavaScript有啥区别,据说还有很多人不知道,来给大家科普一下两者区别! Java 和 JavaScript 不同之处: 1.用处不一样: 它们最本质的不同就是用途:Java目前被广泛 ...

  9. java script的图片隐藏,java和javascript中过滤掉img形式的字符串不显示图片的方法...

    本文实例讲述了java和javascript中过滤掉img形式的字符串不显示图片的方法.分享给大家供大家参考.具体实现方法如下: 1. javascript过滤掉和形式的字符串 复制代码代码如下: 过 ...

  10. java中分支_[Java教程]JavaScript中的分支结构

    [Java教程]JavaScript中的分支结构 0 2016-06-30 23:03:37 说到JavaScript中的分支结构,我们就不得不提到流程控制这个词,我们所有的程序都是由数据和算法组成的 ...

最新文章

  1. 为什么不建议在 MySQL 中使用 UTF-8?
  2. 数据结构读书笔记---------第一章 绪论
  3. boost::geometry::model::point用法的测试程序
  4. DNN 汉化中的问题????
  5. HTML5 Web Storage用法
  6. linux 挂起后无法唤醒屏幕
  7. python解析http数据包_python 3 处理HTTP 请求的包
  8. 印度SaaS创企Whatfix获370万美元A轮融资
  9. python编程师app_新技能:用 Python 写一个安卓 APP
  10. SQL SERVER代理的权限设置
  11. Ubuntu 16.04 状态栏实时显示网速、CPU、内存等
  12. 国企程序员可以干多久
  13. 【mcuclub】红外测温-MLX90614
  14. 用matlab解根3乘根2,信号与系统第五章习题答案
  15. 赵小楼《天道》《遥远的救世主》深度解析(80)强者用逻辑支配行为,弱者用情绪支配行为
  16. 1038 Recover the Smallest Number (30 分)-字符串分段排序
  17. android内存显示修改,涨姿势:修改这个系统选项Android手机再也不用清内存!速度飞起...
  18. Gitlab-CI Runner缓存
  19. 适合软件开发团队的知识管理系统有哪些?10大知识库盘点
  20. ECSHOP去版权教程

热门文章

  1. 《恋上数据结构第1季》二叉树基础、真二叉树、满二叉树、完全二叉树、二叉树的遍历(重点)
  2. 从编写到上线带你搞定springboot博客之编辑博客时新增标签02
  3. SQLSERVER根据字符 切割字符串的方法
  4. ERP财务报表快消失了?用它做财务系统根本不够用
  5. 腾讯智慧校园 php,河南省电化教育馆召开全省“腾讯智慧校园”试点工作推进会...
  6. android jar包冲突_用好这几个技巧,解决Maven Jar包冲突易如反掌
  7. java基础反射知识总结_非常好的Java基础反射总结
  8. juyter显示决策树图形_关于决策树可视化的treePlotter(学习笔记)
  9. python作用域(scope)(三分钟读懂)
  10. python定时器 是线程吗_定时器中断线程python