周小娟
(西安外事學院 計算機中心,陜西 西安 710077)
Matlab是一款優秀的,功能強大的科學計算軟件,它為航空航天,控制,機械,電子,電力,生物,財務等眾多領域提供功能強大的數值計算和仿真工具。Matlab環境下的M語音,簡單靈活,使用方便,但做為一種編程語言,在圖形界面開發方面和Java,C++等高級語音相比卻顯得不夠強大。Matlab雖然提供了一些圖形開發功能,并且利用這些功能開發相對簡單的圖形界面也很受歡迎,但對于稍微復雜一些的圖形界面和大型專業的軟件產品,這些功能就無法實現了。因此在大型專業軟件產品開發中,必須研究一種新的開發方法,將Java,C++等高級語言的圖形界面開發優勢和Matlab數據計算和仿真的優勢結合起來,開發更為專業的軟件產品。幸運的是,Matlab已經提供了和Java,C,C++等高級開發語言的接口,因此將Matlab程序整合進Java,C,C++程序是可行的。
Matlab提供將M程序編譯為C/C++動態庫或者Java包的功能。 在Deployment Tool中用戶可以選擇要編譯的M文件以及編譯結果的類型,然后Deployment Tool將調用編譯器mcc(MATLAB COMPILER COMMAND)對目標文件進行編譯。mcc具有將M文件編譯為C/C++動態庫,Java包,可執行文件的能力,是Matlab和其它語言混合編程中必須使用的一個工具。Matlab還提供另外一個工具mbuild將C/C++程序編譯為可執行文件或者動態庫,mbuild內部將調用操作系統已經安裝的編譯器進行編譯。
Java語言具有優秀的圖形界面開發能力,常用的圖形開發庫有基于 AWT(Abstract Window Toolkit)的 Java-Swing 開發技術和基于 SWT(Standard Widget Toolkit)的 Eclipse開發技術。AWT具有和Java類似的和操作系統無關的特征,因此在Windows上開發的AWT程序,在Linux上運行具有相同的界面風格。SWT使用操作系統提供的圖形界面,和程序運行的操作系統具有直接關系,因此在Windows上開發的SWT程序,具有Windows操作系統的風格,在Linux上運行時具有Linux操作系統的風格。因為SWT直接調用的是操作系統本身的圖形庫,因此SWT可以提供完全的圖形界面,功能比AWT要強大的多。本文將使用SWT在W indowsXP操作系統上進行Java程序圖形界面的開發。
在Java中調用Matlab程序有兩種方法:1.將Matlab程序編譯為Java包,然后在Java程序中調用Java包中的類和方法;2.將Matlab程序編譯為C/C++動態庫,然后通過JNI(Java Native Interface)在Java程序中調用。第一種方法的實施比較簡單,直接在Matlab中調用Deployment Tool就可以完成;第二種方法的實施比較復雜一些,需要編寫Java Native類和C/C++代碼,但在執行效率方面具有優勢。因此在這兩種方法的選擇上以程序的規模和效率為參考進行,如果Matlab程序的規模比較小,處理的數據量比較小,可以選擇第一種方法,反之選擇第二種方法。對于執行效率比較重要的程序來說,可以用兩種方法來實現混合編程,然后通過對比選擇效率高的一種方法進行實施。下面對這兩種方法的實現過程分別進行描述。
將Matlab程序編譯為Java包,然后在Java程序中調用Java包中的類和方法,這種混合編程方法相對比較簡單。具體步驟如下:
1)在Matlab命令行輸入deploytool啟動Deployment Project對話框。
2)在對話框中輸入編譯項目名稱及其位置,并選擇Java Package為編譯類型。

圖1 創建編譯項目Fig.1 Create a deployment project
3)在Java Package頁面中,通過添加類為要生成的Java類命名,然后通過添加文件為這個類添加相關的M文件。

圖2 為編譯項目命名要創建的Java類并添加M文件Fig.2 Add Java class and M files for the deployment project
4)點擊Java Package頁面右上方的Build按鈕進行編譯。
編譯完成后,在編譯項目所在目錄將生成一個以編譯項目名稱命名的目錄,在這個目錄內有兩個子目錄distrib和src。distrib子目錄放置編譯好的Java包和幫助文檔,這些都是我們期望的結果,其中Java包就是混合編程中Java一方需要用到的唯一文件。src子目錄內放置的是Java包中類的代碼文件,這些文件是中間產物,混合編程中Java一方不需要這些文件。因此一般不需要關心src子目錄中的產物。
混合編程中在Matlab一方編譯生成Java包后,Matlab的編程任務就完成了。下面需要在混合編程中Java一方調用該Java包中的類和方法。具體的調用方法如下,
1)將Matlab生成的Java包包含在Java項目的classpath中。這就保證了Java包中的類在當前Java項目內可以訪問到;
2)在調用Matlab算法的Java類文件中導入com.mathworks.toolbox.javabuilder.*和MATLAB編譯生成的Java類;
import com.mathworks.toolbox.javabuilder.*;
import rankine_package.*;
3)在調用Matlab算法的Java類文件就可以直接使用MATLAB編譯生成的類了;
rankineClass MLRankine=new rankineClass();
Object[]Output=MLRankine.rankine(1, Input);
這個例子中,rankineClass就是在Matlab中編譯生成的Java類,其方法rankine()就是M文件rankine.m中實現的Matlab算法,第一個參數的值1表示這個算法的返回值只有一個。
將Matlab程序編譯為C/C++動態庫,然后在C/C++程序中調用該動態庫生成另外一個動態庫,最后通過JNI(Java Native Interface)在Java程序中調用Matlab程序中的函數對應的C/C++函數,這種混合編程方法相對比較復雜。具體步驟如下:
1)首先必須選擇并設置Matlab中mcc和mbuild在編譯時使用的C/C++編譯器。通過在Matlab中輸入 “mbuild–setup”,Matlab會把當前操作系統已經安裝的C/C++編譯器列出來,這時候只需要選擇要使用的一個編譯器就可以了。如果當前系統沒有安裝任何編譯器,需要安裝一個。在Linux操作系統上,可以安裝gcc。在Windows操作系統上,可以安裝Microsoft Visual Studio for C/C++。
2)在設置好編譯器后,就可以啟動deployment tool將要使用的M文件編譯為C/C++動態庫了。編譯過程和2.1節一樣,只不過編譯類型需要選擇C動態庫,或者C++動態庫。編譯結果中distrib目錄中有4個結果文件在后續的步驟中要用到:一個*.dll文件,這是編譯的結果動態庫包含MATLAB程序對應的函數;一個*.h文件,動態庫中函數的列表,在調用動態庫的程序中需要用到;一個*.lib文件,動態庫對應的靜態庫;一個*.exports文件,羅列了所有*.h中的函數名稱。在*.h文件中,每一個Matlab程序的函數都有一個對應的C/C++函數,除此之外,還有兩個函數也必須被用到,它們是xxxInitialize()和 xxxTerminate(),其中 xxx 是編譯項目的名稱。xxxInitialize()必須在調用任何Matlab函數對應的C/C++函數之前調用,xxxTerminate()必須在所有調用完成后,程序結束前調用。
3)在編譯完 Matlab程序后,就可以進行 JNI(Java Native Interface)程序的開發了,通過JNI程序實現在Java圖形界面程序中調用C/C++動態庫。
①首先必須在Java圖形界面程序中聲明一個Java類包含若干native方法。這些native方法中,必須有兩個native方法分別和 xxxInitialize()和 xxxTerminate()對應,其它的 native方法分別和*.h中要被用到的函數對應。下面就是一個這樣的類聲明的例子,
在這個例子中,通過System.load()函數裝載的C/C++動態庫libjnirankine.dll就是JNI要生成的動態庫。這個動態庫實現了這個Java類中的所有native函數,而這些native函數分別調用了Matlab程序編譯結果動態庫中的函數。

圖3 一個包含native函數的Java類的例子Fig.3 A Java class example with native methods
②通過javac命令將這個Java類進行編譯生成jniRankine.class,然后調用javah–jni jniRankine生成一個*.h文件jniRankine.h。javah工具將所有native函數轉化為了JNI中的C/C++函數。這時候需要開發者手動生成一個jniRankine.c或者jniRankine.cpp來實現jniRankine.h中的所有函數。jniRankine.c或者jniRankine.cpp必須包含Matlab程序編譯結果中的*.h文件,然后在jniRankine.c或者jniRankine.cpp中的各個函數中調用對應的Matlab程序編譯結果中的C/C++函數。這樣就完成了JNI的編程。最后通過在Matlab中運行如下mbuild命令編譯jniRankine.h以及jniRankine.c或者jniRankine.cpp,并鏈接Matlab程序編譯結果庫,生成 libjnirankine.dll,
mbuild-I“C:Program FilesJavajdk1.7.0_05include” -I“C:Program FilesJavajdk1.7.0_05includewin32” jnirankine.cpp rankineDll.exports rankineDll.lib
-link shared-output libjnirankine
在這個命令中I參數后面是JDK的安裝路徑,rankine Dll.exports和rankineDll.lib是Matlab程序編譯的結果,libjnirankine是編譯結果。
③最后在Java圖形界面程序中就可以像使用一般的Java類一樣調用jniRankine類中的方法進而應用Matlab程序中的算法了。必須保證Matlab編譯結果中的*.dll文件在java.library.path中可以找到。
文中使用第1章中描述的兩種方法分別實現了計算理想朗肯循環的熱力學性質的程序,程序比較簡單,方便讀者明白程序結構和流程。程序的開發環境為Eclipse和Matlab。在開發機器上安裝了JDK1.7.0_05來支持Eclipse環境和第一種方法中的Java包的編譯;在開發機器上還安裝了Microsoft Visual Studio10.0 for C++來支持第二種方法中C++程序的編譯。

圖4 理想朗肯循環的熱力學性質計算程序的圖形界面Fig.4 GUI of thermodynamic efficiency of Ideal Rankine Cycle calculating program
程序的界面通過使用SWT圖形開發技術開發,如圖4所示,在WindowsXP操作系統上運行時,SWT程序界面風格和WindowsXP保持一致。在窗口的左邊部分是計算時的輸入參數,“Load Defaults”按鈕會自動給各個輸入參數填充默認值。點擊 “Calculate”按鈕程序就會調用Matlab程序的算法進行熱力學性質的計算,并將計算結果顯示在窗口的右半部分的輸入框中。
Matlab的基本構成和功能決定了Java-Matlab混合編程的可行性,利用這種混合開發技術可以開發出功能強大,界面友好,具有良好擴展性和維護性的模塊化的科學計算程序。這種混合開發技術可以使得進行圖形界面開發的Java程序員和進行數值運算開發的Matlab程序員分離開來,彼此專注于自己擅長的方面,降低的開發難度,提高了開發效率。
[1]Hanselman D,Littlefield B.精通MATLAB7[M].北京:清華大學出版社,2006.
[2]李傳軍.C語言與MATLAB接口--編程與實例[M].北京:北京郵電大學出版社,2004.
[3]羅星月.Matlab與C語言程序的應用編程接口[EB/OL].(2007).http://www.builder.com.cn/2007/1030/591033.shtml.
[4]Muller Held.JMatLink[EB/OL].(2006).www.held-muellerde/JMatLink/.
[5]The Mathworks,Inc.Matlab幫助文檔:Matlab builder for Java[S].2007
[6]廖云伢,王建新,盛羽.基于Java與Matlab集成的虛擬實驗平臺的設計與實現[J].計算機應用,2007,27(2):394-399.LIAO Yun-ya,WANG Jian-xin,SHENG Yu.Design and implementation of virtual lab platform based on integration of java and matlab[J].Journal of Computer Applications,2007,27(2):394-399.
[7]張崇明,汪春梅,朱品昌,等.使用Java擴展Matlab[J].計算機工程與設計,2006,27(9):1563-1565.ZHANG Chong-ming,WANG Chun-mei,ZHU Pin-chang,et al.Extending matlab using java[J].Computer Engineering and Design,2006,27(9):1563-1565.