Matlab/Octave中使用Java

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
  • javaMethod

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进行一些预处理(数据格式变换等等)或者后处理(画图,生成报告等),这样可以充分发挥二者在各自领域的优势)。

1. Java类导入到Matlab Workspace

为了正确导入Java类,首先需要在Matlab中正确配置Java的classpath。主要有以下几种方式:

  1. 添加到静态classpath
    在Matlab命令行中输入edit classapth.txt,将用户自定义的classpath路径添加到文件末尾。
  2. 添加到java.ext.path目录
    执行java.lang.System.getProperty('java.ext.dirs')命令获取Java扩展目录。将用户自己的jar包到ext目录中。
  3. 使用动态classpath
    Matlab提供了javaclasspathjavaaddapthjavarmpath等命令来动态配置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对象。

2. 查看Java类的相关信息

查看Java类的方法

methods java.util.Date -full
methodsview java.util.Date

查看Java类的字段

fieldnames(java.util.Date)

3. Matlab中使用Java对象

首先创建一个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

4. 实例1:利用Java Swing实现高级GUI

下面我们来看一个利用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

5. 实例2:用户自定义Java类的调用

以上例子演示的是在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下载

你可能感兴趣的:(Matlab/Octave)