Matlab对于混合编程提供了广泛的支持,例如,Matlab通过mex编程方式可以调用Fortran或者C语言编写的mex文件;而通过mcc可以将Matlab的m文件编译为可被C/C++调用的动态链接库或者独立的可执行文件。此外,Matlab还提供了对Java、COM、.net以及excel等的支持,可以实现各种具有创意的混合编程形式。通过Matlab和Java混合编程,可以实现在Matlab中调用用户自定义的jar,或者在Java程序中调用Matlab代码。
本文就来谈谈在Matlab中调用Java的相关话题。由于Octave可以视作Matlab的开源替代品,大多数Matlab代码也可以几乎不做改变地在octave中运行,所以本文中将Octave和Matlab相提并论。为了保证二者之间的可移植性,本文只讨论对二者都适用的话题。
Matlab/Octave中都提供对以下两个函数的支持:
javaObject
用于生成Java对象,而javaMethod
可以调用Java对象的方法。
利用这两个方法,可以在Matlab/Octave中轻松调用Java代码,由于Java含有丰富的第三方库,从而可以极大扩展Matlab的功能。例如,利用Java进行更加高级的GUI设计,Java调用zxing识别二维码或者在Matlab/Octave中调用利用Java开发的科学应用程序(举个例子,现在很多大数据应用程序都是利用Java/Scala这类语言开发,发布时打包为一个jar包,提交到Hadoop/Spark集群上运行。利用Matlab对Java的支持,我们可以基于Matlab平台配置一个Hadoop应用程序的测试环境。即,利用Java开发算法核心算法代码并打包,在Matlab中调用jar包运行。运算前后也可以在Matlab进行一些预处理(数据格式变换等等)或者后处理(画图,生成报告等),这样可以充分发挥二者在各自领域的优势)。
为了正确导入Java类,首先需要在Matlab中正确配置Java的classpath。主要有以下几种方式:
edit classapth.txt
,将用户自定义的classpath
路径添加到文件末尾。java.ext.path
目录 java.lang.System.getProperty('java.ext.dirs')
命令获取Java扩展目录。将用户自己的jar包到ext目录中。javaclasspath
,javaaddapth
,javarmpath
等命令来动态配置Java的classpath。前两种方式需要重启Matlab才能生效,而第三种方法可以立即生效。如果只是为了测试,建议使用动态classpath方式。因为前两种方式都会修改Matlab的默认配置,而为了系统的安全,最好只对其做最小的修改。
通过javaclasspath('dynamic')
和javaclasspath('static')
可以查Java classpath的当前配置。
接下来就可以使用import
命令将Java类导入到Matlab工作空间:
import java.util.Date
import org.apache.log4j.Logger
若输入import
,不加其他参数,则查看当前所有导入的Java类,利用clear import
清除导入的Java类。
使用[mObj, mexObj, javaObj] = inmem()
还可以查看内存中当前已有的Java对象,即javaObj
中的内容,而clear java
清除内存中的Java对象。
查看Java类的方法
methods java.util.Date -full
methodsview java.util.Date
查看Java类的字段
fieldnames(java.util.Date)
首先创建一个Java对象:
dateObj = java.util.Date()
或者使用:
dateObj = javaObject('java.util.Date')
查看Java对象类型:
class(dateObj)
isjava(dateObj)
调用Java对象的方法,有以下3种形式:
dateObj.toString()
toString(dateObj)
javaMethod('toString', dateObj)
关于以上涉及的各个Matlab命令的详细说明参考Matlab帮助文档,例如
doc javaObject
doc javaMethod
下面我们来看一个利用Java Swing实现高级GUI的例子。程序利用Java Swing的Frame显示一个无边框图片窗口,3秒后窗口关闭。这可以很容易应用到程序的启动动画中。例如,Matlab启动时显示的spalash就可以这样实现。代码如下:
ImageFile = 'ngc6543a.jpg';
ScreenSize = get(0, 'ScreenSize');
jImage = im2java(imread(ImageFile));
jfBounds = num2cell([...
(ScreenSize(3)-jImage.getWidth)/2 ...
(ScreenSize(4)-jImage.getHeight)/2 ...
jImage.getWidth ...
jImage.getHeight]);
jFrame = javax.swing.JFrame;
icon = javax.swing.ImageIcon(jImage);
label = javax.swing.JLabel(icon);
jFrame.getContentPane.add(label);
jFrame.setUndecorated(true)
jFrame.setBounds(jfBounds{:});
jFrame.pack
jFrame.show
pause(3)
jFrame.dispose
以上例子演示的是在Matlab中调用Java标准类,几乎没有什么难度,也不会出现什么问题。然而,当尝试在Matlab中调用用户自定义的Java类时,情况往往就不是那么简单了。
为了测试,我们创建一个简单的Java类abc.Point
。
// Point.java
package abc;
public class Point {
double x;
double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
public String toString() {
return "(" + x + "," + y +")";
}
public void display() {
System.out.println("Coordinate: " + toString());
}
public static void main(String args[]) {
new Point(1,2).display();
}
}
接下来我们将其打包,生成point.jar文件。
最简单的是使用Eclipse编译并打包。由于此处只有一个Java文件,我们选择直接在命令行下完成文件的编译和打包。
首先编译Point.java
:
javac -d . -s . Point.java
这会在当前目录自动生成abc文件夹,里面是Point.class文件。
我们可以先测试一下Point.class
,执行java abc.Point
,输出Coordinate: (1.0,2.0)
,正常。
再利用jar命令打包:
jar -cvf point.jar -C . abc Point.java
其中-C . abc Point.java
表示将当前目录下的abc文件夹和Point.java文件打包到待生成的point.jar文件中。
接下来将point.jar复制到Matlab环境中进行测试:
base = pwd
ext = {[base], [base '/point.jar']};
javaaddpath(ext)
import abc.Point
很不幸,在执行import abc.Point
时会报错,Matlab提示找不到abc.Point
类。而通过·javaclasspath
命令查看当前的javaclasspath配置,却发现当前目录以及point.jar确实已经加入了j动态avaclasspath路径中。
而且执行import abc.*
也是正常的,不会报错。而如果将point.jar复制到Java ext目录中,执行import abc.Point
时会抛出异常java.lang.UnsupportedClassVersionError
。
这其实就是执行import abc.*
失败的真正原因,因为用户自定义jar包中的class文件和Matlab的jre版本不兼容,导致Matlab的jre无法加载用户jar包中的class文件。具体来说,Matlab中的jre版本比较老(我的Matlab是R2010,jre版本为1.5),而我编译Java代码以及打包使用的JDK是1.7的。关于JDK版本兼容的话题,可以参考这篇帖子。
这一问题是大多数尝试在Matlab中调用Java的人都会遇到的一个问题,因此这里特别提出来进行说明。
下面说一下怎么解决。其实思路很简单,既然是JDK版本不兼容问题,那当然是采用兼容的版本了。
如果使用Eclipse编译和打包,设定compiler时选择版本1.5。而如果使用命令行的方式编译和打包,则需要通过javac
的-target
选项指定生成的class文件的版本,这里为了保证兼容性,选择1.5。对应的命令为:
javac -d . -s . -source 1.5 -target 1.5 Point.java
jar -cvf point.jar -C . abc Point.java
之后再将生成的jar包放到Matlab中进行测试,发现此时abc.Point
可以正常导入了。
最后,附上测试用的Matlab代码:
% MatlabCallJavaDemo.m
base = fileparts(mfilename('fullpath'));
exts = {[base], [base '/point.jar']};
% Add this path to java dynamic classpath
javaclasspath(exts)
% alternatively, the following statement is also OK
% javaaddpath(ext)
% show java dynamic classpath
javaclasspath('-dynamic')
%% Test case 1: import class
import('abc.Point')
import
% show methods of abc.Point class
methods abc.Point
methods('abc.Point', '-full')
methodsview('abc.Point')
point = Point(1, 2)
%% Test case 2: create java object and invoke methods
point = abc.Point(1, 2);
x = point.getX()
y = getY(point)
%% Test case 2: create java object and invoke methods using javaObject and javaMethod
point = javaObject('abc.Point', 1, 2)
javaMethod('display', point)
x = javaMethod('getX', point)
y = javaMethod('getY', point)
% alternatively. you can remove the exts from javaclasspath
clear point
for i = 1:numel(exts)
javarmpath(exts{i})
end
完整代码可以从CSDN下载