运行环境:
MATLAB R2022a
Java 8(1.8.0_311)
Windows 10 教育版 64位
使用混合编程通常都不是好主意,但是有时候会遇到极端的情况。Java 擅长网络编程,MATLAB 擅长数学高级计算与图形化。这种情况下,没办法使用一种编程语言快速完成这两项事情,因此不得不使用 Java、MATLAB 混合编程。这里提供的办法是,将一个 MATLAB 函数文件转化为 Java 的 JAR 包,然后在 Java 中运行这个 JAR 包。
编写这个教程时,笔者已经帮读者几乎踩遍了所有常见的坑。仔细阅读本教程可以减少很多麻烦。
编写 MATLAB 源文件
-
这里编写了一个简单的 MATLAB 函数 matlabPlot、matlabPolarplot。这两个函数做的事情很简单,只是调用原生函数用于绘图而已。
-
matlabPlot.m
function matlabPlot(x,y) plot(x,y); axis equal;
-
matlabPolarplot.m
function matlabPolarplot(x,y) polarplot(x,y);
-
安装 Java
-
下面来开始将这个函数文件转化成 JAR 包。在这之前,需要安装 JDK。关于这方面的内容,可见笔者的另一篇博客:
Java 的下载安装教程:
https://blog.csdn.net/wangpaiblog/article/details/111466827
【踩坑提醒】
-
直到
MATLAB R2022a
时,安装的 Java 版本不能超过 8。 -
需要将 Windows 环境变量
JAVA_HOME
的值设成 Java 8 的安装目录。 -
配置完 Windows 环境变量后,重启 MATLAB 之后才会在 MATLAB 中生效。
-
制作 JAR 包
-
在 MATLAB 命令行窗口输入以下命令。
deploytool
-
在弹出的窗口中选择 Libary Compiler。
-
在弹出的窗口中选择 Java Package,并添加前面的 MATLAB 函数文件,然后依次填写好 Java 类名等信息,最后点击 Package 生成 JAR 包。
-
首次打包时,需要按提示保存本次打包配置(格式为
.prj
)。 -
打包成功会出现如图结果。如果打包失败,需查看失败日志判断原因。比方说,如果没注意笔者前面编写的
踩坑提醒
,则会因为 JDK 的问题引发失败。 -
如果打包成功,MATLAB 会在与前面打包配置文件同目录下,生成一个以配置文件名为名称的一个文件夹。该文件夹有以下文件(夹):
-
for_redistribution
-
for_redistribution_files_only
-
for_testing
-
PackagingLog.html
这里不详细介绍各个文件的作用。我们所需要的 JAR 包在文件夹
for_redistribution_files_only
下。其它文件均可删除。 -
找到 MATLAB 的 JAR 包
-
但是,
光有此 JAR 包还不能在我们自己的 Java 程序中运行
。因为显然,此 JAR 包本质上只会含我们上面写的那么一点儿代码,这肯定是无法运行的。运行肯定还需要 MATLAB 自身对外提供的 SDK,也就是编程时经常所说的运行环境。不过幸运的是,这个 Java 版本的 SDK 在 MATLAB 安装的时候就已经提供了。对于笔者的
MATLAB R2022a
,它在如下目录中。读者需要根据自己 MATLAB 的安装情况找到那个名为
javabuilder.jar
的 JAR 包。C:\Program Files\MATLAB\R2022a\toolbox\javabuilder\jar\javabuilder.jar
-
因此,这里只需要
MatlabUtil.jar
和
javabuilder.jar
即可在我们自己的 Java 程序中运行。
【踩坑提醒】
这两个 JAR 包不能在 Java 8 以上的版本运行,否则会发生如下报错:
Exception in thread "AWT-EventQueue-0": java.lang.IllegalAccessError: superclass access check failed: class com.mathworks.hg.peer.types.HGMotifCheckMenuUI (in unnamed module @0x706bf110) cannot access class com.sun.java.swing.plaf.motif.MotifMenuUI (in module java.desktop) because module java.desktop does not export com.sun.java.swing.plaf.motif to unnamed module @0x706bf110 at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524) at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427) at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421) at java.base/java.security.AccessController.doPrivileged(AccessController.java:712) at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) at com.mathworks.hg.peer.MenuPeer.doCreateMenu(MenuPeer.java:142) at com.mathworks.hg.peer.MenuPeer.access$000(MenuPeer.java:32) at com.mathworks.hg.peer.MenuPeer$1.run(MenuPeer.java:131) at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.runit(HGPeerQueue.java:290) at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.runThese(HGPeerQueue.java:318) at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.run(HGPeerQueue.java:335) at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:741) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Java 允许在同一个操作系统上安装多个版本的 Java。由于 Java 的 IDE 普遍支持在 IDE 中自由选择 Java 版本,所以这可能导致问题。
如果想确认自己的 Java 程序使用的是哪个版本的 Java,可以在程序使用以下代码之一。
System.out.println("Java 版本号:" + System.getProperty("java.version")); System.out.println("Java 虚拟机规范版本号:" + System.getProperty("java.vm.specification.version")); System.out.println("Java 规范版本号:" + System.getProperty("java.specification.version")); System.out.println("Java 类路径:" + System.getProperty("java.class.path")); System.out.println("Java lib 路径:" + System.getProperty("java.library.path")); System.out.println("Java 执行路径:" + System.getProperty("java.ext.dirs"));
在 Java 中调用 MATLAB 的 JAR 包
关于这方面的内容,可见笔者的另一篇博客:
如何在 Java 中调用 MATLAB 代码:
https://blog.csdn.net/wangpaiblog/article/details/128293573