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下載

發佈了18 篇原創文章 · 獲贊 43 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章