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。主要有以下幾種方式:
- 添加到靜態classpath
在Matlab命令行中輸入edit classapth.txt
,將用戶自定義的classpath
路徑添加到文件末尾。 - 添加到
java.ext.path
目錄
執行java.lang.System.getProperty('java.ext.dirs')
命令獲取Java擴展目錄。將用戶自己的jar包到ext目錄中。 - 使用動態classpath
Matlab提供了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對象。
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下載