如何在 Java 中调用 MATLAB 代码

news2024/11/16 3:23:20

文章目录

  • 测评
  • 完整源代码

运行环境:

  • MATLAB R2022a

  • Java 8(1.8.0_311)

  • IntelliJ IDEA 2022.2.1 (Ultimate Edition)

  • Maven 3.8.3

  • Windows 10 教育版 64位

  使用混合编程通常都不是好主意,但是有时候会遇到极端的情况。Java 擅长网络编程,MATLAB 擅长数学高级计算与图形化。这种情况下,没办法使用一种编程语言快速完成这两项事情,因此不得不使用 Java、MATLAB 混合编程。这里提供的办法是,将一个 MATLAB 函数文件转化为 Java 的 JAR 包,然后在 Java 中运行这个 JAR 包。

  在 Java 中调用 MATLAB 代码,方法其实有很多,这里给出的是一种朴素的方法:先将 MATLAB 代码转化为 Java 的 JAR 包,然后在 Java 程序中就可以直接调用这个 JAR 包了。

  编写这个教程时,笔者已经帮读者踩了很多坑。仔细阅读本教程可以减少很多麻烦。

  1. 这里编写了一个简单的 MATLAB 函数 matlabPlot、matlabPolarplot。这两个函数做的事情很简单,只是调用原生函数用于绘图而已。只不过一个是以直角坐标系作图,另一个是极坐标系。

    • matlabPlot.m
    function matlabPlot(x,y)
    plot(x,y);
    axis equal;
    
    • matlabPolarplot.m
    function matlabPolarplot(x,y)
    polarplot(x,y);
    
  2. 下面需要将上面那两个 MATLAB 函数文件打包为 JAR,类名为 MatlabUtil。关于这方面的内容,可见笔者的另一篇博客:

    如何将 MATLAB 源代码导出成 Java 的 JAR 包:
    https://blog.csdn.net/wangpaiblog/article/details/127957144

  3. 现在假设读者已经完成了打 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
    
  4. 因此,这里只需要 MatlabUtil.jarjavabuilder.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"));
    

  5. 现在需要将上面那两个 JAR 包导入到我们的 Java 项目中去。根据读者使用的 Java 构建工具的不同,这个过程会有不同。

    • 对于不使用构建工具的情况,可见笔者的另一篇博客:

      Java 库文件的添加教程:
      https://blog.csdn.net/wangpaiblog/article/details/111772193

    • 如果使用构建工具 Maven,可将其作为本地依赖。更多的信息,可见笔者的另一篇博客:

      如何在 Maven 中通过本地路径使用 JAR 包依赖:
      https://blog.csdn.net/wangpaiblog/article/details/127840334

  6. 现在假设读者已经完成了导入 JAR 包的步骤。可以开始编写自己的 Java 程序了。为了使用到上面的直角坐标系作图函数,这里编写了一个绘制 双曲螺线 的 Java 代码,但是本文不打算详细介绍双曲螺线。

    • 双曲螺线参数方程:(x、y 分别为横、纵坐标)

      在这里插入图片描述

    • 双曲螺线 Java 代码:

      /**
       * 双曲螺线
       *
       * @since 2022-10-16
       */
      public static void hyperbolicSpiral() throws MWException {
          final long startTime = System.currentTimeMillis();
          double start = PI;
          double end = 1000 * PI;
          double interval = PI / 100;
          int pointNum = (int) ((end - start) / interval);
          int[] dimensions = {1, pointNum};
          MWNumericArray x = MWNumericArray.newInstance(dimensions, MWClassID.DOUBLE, MWComplexity.REAL);
          MWNumericArray y = MWNumericArray.newInstance(dimensions, MWClassID.DOUBLE, MWComplexity.REAL);
          final double c = 100;
          for (int i = 1; i <= pointNum; ++i) {
              double ti = i * interval + start;
              double xi = c * cos(ti) / ti;
              x.set(i, xi);
              double yi = c * sin(ti) / ti;
              y.set(i, yi);
          }
          MatlabUtil matlabUtil = null;
          try {
              matlabUtil = new MatlabUtil();
              matlabUtil.matlabPlot(x, y); // 在 MATLAB 绘制完图像界面之后,此方法会返回
              System.out.printf("双曲螺线绘制用时:%fs%n", (System.currentTimeMillis() - startTime) / 1000.0);
              matlabUtil.waitForFigures(); // 在用户关闭 MATLAB 图像界面之前,此方法会阻塞当前线程
          } finally {
              // 释放 MATLAB 图像界面资源。一旦释放之后,当前 MATLAB 图像界面会被迫关闭
              MWArray.disposeArray(x);
              MWArray.disposeArray(y);
              if (matlabUtil != null) {
                  matlabUtil.dispose();
              }
          }
      }
      
    • 程序运行结果:

      在这里插入图片描述

  7. 现在来试一试极坐标绘图。这里选择用极坐标绘制 蝴蝶曲线

    • 蝴蝶曲线极坐标方程:(x、y 分别为极角、极径坐标)

      在这里插入图片描述

    • 蝴蝶曲线 Java 代码:

      /**
       * 蝴蝶曲线
       *
       * @since 2022-10-16
       */
      public static void butterflyCurve() throws MWException {
          final long startTime = System.currentTimeMillis();
          double start = 0;
          double end = 20 * PI;
          double interval = PI / 50;
          int pointNum = (int) ((end - start) / interval);
          int[] dimensions = {1, pointNum};
          MWNumericArray x = MWNumericArray.newInstance(dimensions, MWClassID.DOUBLE, MWComplexity.REAL);
          MWNumericArray y = MWNumericArray.newInstance(dimensions, MWClassID.DOUBLE, MWComplexity.REAL);
          for (int i = 1; i <= pointNum; ++i) {
              double xi = i * interval + start;
              x.set(i, xi);
              double yi = exp(cos(xi - PI / 2)) - 2 * cos(4 * (xi - PI / 2)) + pow(sin((xi - PI / 2) / 12), 5);
              y.set(i, yi);
          }
          MatlabUtil matlabUtil = null;
          try {
              matlabUtil = new MatlabUtil();
              matlabUtil.matlabPolarplot(x, y); // 在 MATLAB 绘制完图像界面之后,此方法会返回
              System.out.printf("蝴蝶曲线绘制用时:%fs%n", (System.currentTimeMillis() - startTime) / 1000.0);
              matlabUtil.waitForFigures(); // 在用户关闭 MATLAB 图像界面之前,此方法会阻塞当前线程
          } finally {
              // 释放 MATLAB 图像界面资源。一旦释放之后,当前 MATLAB 图像界面会被迫关闭
              MWArray.disposeArray(x);
              MWArray.disposeArray(y);
              if (matlabUtil != null) {
                  matlabUtil.dispose();
              }
          }
      }
      
    • 程序运行结果:

      在这里插入图片描述

测评

  程序编写完之后,评估一下性能是一件顺理成章的事情。

  为此,笔者之前留了一手,在前面的 Java 程序插入了程序运行时间统计。运行结果表明,在 Java 中调用 MATLAB 绘图,两个程序的运行时间都大致在 13s 左右(根据不同机器的运行环境,此结果仅供参考)。

  现在来试试其在 MATLAB 中的运行情况。

  为此,这里同样对应编写了两个 MATLAB 脚本。这两个脚本的内容非常简单,这里不作解释。

  • hyperbolicSpiral.m

    clear
    clc
    
    tic;
    
    t=pi:pi/100:1000*pi;
    c=100;
    x=c*cos(t)./t;
    y=c*sin(t)./t;
    plot(x,y);
    axis equal;
    
    toc;
    
  • butterflyCurve.m

    clear
    clc
    
    tic;
    
    x=0:pi/50:20*pi;
    y=exp(cos(x-pi/2))-2*cos(4*(x-pi/2))+sin((x-pi/2)/12).^5;
    polarplot(x,y);
    
    toc;
    

  这两个脚本的运行结果表明,hyperbolicSpiral、butterflyCurve 的运行时间均大致为 0.07 秒。而如果之前没有关闭上一次的 MATLAB 图像界面,这个时间可以分别降到 0.02、0.01 秒(根据不同机器的运行环境,此结果仅供参考)。

  可以看出,就算是算作最坏情况——第一次运行(没有借用上一次的 GUI 资源),使用 MATLAB 原生方式运行的时间比使用 Java 大概快了 200 倍!

  我们知道,MATLAB 的核心是用 C 语言编写的,这是不是说 Java 比 C 语言慢 200 倍呢?这种说法可不太精确。作为编程人员,我们知道,Java 是比纯 C 语言要慢。不过,作为一段程序,它的运行时间由最耗时的那段决定。对于这种需要 GUI 界面显示图像的程序,如果计算过程很简单的话,运行时间都主要耗在 GUI 上。在 Java 8 中,最先进的 GUI 就是 Swing 了。MATLAB 使用 Java 完成的图像显示就是使用 Swing 完成的。

  GUI 一直 Java 的软肋。作为 Java 的 GUI 技术 Swing、JavaFX 的长期使用人员,笔者可以很遗憾地告诉你,Java 的 GUI 应用无论是在启动时间、内存占用上,都比 C 家族编写的 GUI 应用逊色很多。因此,上面这之所以会出现这种离谱的现象,是因为它们各自在 UI 上耗费了大量的时间,而后台计算部分又过于简单。所以,如果编写的程序涉及计算的部分足够复杂,使用 Java 运行的时间也不会出现大幅度的增长。只能说,如果选择 Java 调用 MATLAB 绘图,不管程序有多简单,至少都要耗费 13 秒的时间。

  那么,读者就可能想问了,使用 Java 不仅运行速度慢,编写的代码还“又臭又长”,直接使用 MATLAB 不就可以了吗?这又回到文中开始的问题。混合编程一直都是不应提倡,能避免就避免。但笔者编写的很多程序,它们同时需要使用 Python 进行网上爬虫、使用 Java 连接数据库和服务器、使用 MATLAB 进行绘图展示。这无法通过纯 MATLAB 代码来完成其它操作。就算 MATLAB 提供了支持,使用起来也势必有很多短板。再加上笔者在此之前已经掌握了很多门编程语言,因此再学习新的语言也不是太有难度。

完整源代码

  本文上面演示的完整代码已上传至 GitHub 中,可免费下载。同时笔者也将不定期免费进行升级维护,更新的内容也会即时上传。GitHub 地址:https://github.com/wangpaiblog/20221212_java-calls-matlab

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83343.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git与github安装与配置

git与github安装与配置 最近总是害怕电脑和实验代码突然出事&#xff0c;想了想&#xff0c;拖了很久的git给搞好&#xff0c;所以花了几个小时&#xff0c;从入门到搭建好&#xff0c;这篇文章与其他文章来说&#xff0c;总体上是大同小异&#xff0c;算是前人的一个总结&…

【python基于mysql数据库实现无页面学生信息管理系统】

python基于mysql数据库实现无页面学生信息管理系统以及简单操作mysql前言一、分步讲解0.创建两张数据表1.大概思路2.首先连接数据库3.查看所有学生成绩信息4.查看所有学生个人信息5.查看指定学生成绩信息6.添加学生个人信息7.添加学生成绩信息8.修改学生个人信息9.修改学生成绩…

多模态融合技术

多模态融合的主要目标是缩小模态间的异质性差异&#xff0c;同时保持各模态特定语义的完整性&#xff0c;并在深度学习模型中取得较优的性能。 一、多模态融合架构 多模态融合架构分为&#xff1a;联合架构&#xff0c;协同架构和编解码器架构。联合架构是将单模态表示投影到…

数据库运维 | 携程分布式图数据库NebulaGraph运维治理实践

作者简介&#xff1a;Patrick Yu&#xff0c;携程云原生研发专家&#xff0c;关注非关系型分布式数据存储及相关技术。 背景 随着互联网世界产生的数据越来越多&#xff0c;数据之间的联系越来越复杂层次越来越深&#xff0c;人们希望从这些纷乱复杂的数据中探索各种关联的需求…

[附源码]Python计算机毕业设计大学生心理健康咨询系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

(免费分享)基于jsp,ssm酒店管理系统

开发工具&#xff1a;eclipse&#xff0c;mysql5.7 Tomcat8.0&#xff0c;jdk1.8 系统分用户前台和管理后台两部分 前台截图&#xff1a; 后台截图&#xff1a; package com.mypower.controller;import java.io.IOException; import java.io.OutputStream; import java.io.P…

行业如此内卷,品牌如何实现可持续招商增长?

文|螳螂观察&#xff08;TanglangFin&#xff09; 作者|陈小江 2019年&#xff0c;厦门的一位单亲妈妈&#xff0c;听了一堂课&#xff0c;驱车十几个小时抵达南京&#xff0c;就为找一个人。 找一个&#xff0c;能救企业的人。 由于股权融资对赌失败&#xff0c;这位原本坐…

【C初阶】第八篇——结构体

结构体的声明 什么是结构体 结构体的声明 结构成员的类型 结构体变量的定义和初始化 结构体成员的访问 结构体传参 结构体的声明 什么是结构体 结构体是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同类型的变量. 结构体的声明 struct tag {member - list…

艾美捷人乳铁蛋白ELISA试剂盒预期用途注意事项

艾美捷Kamiya人乳铁蛋白ELISA试剂盒预期用途&#xff1a; 人乳铁蛋白ELISA是一种高度灵敏的双位点酶联免疫测定&#xff08;ELISA&#xff09;人类生物样品中乳铁蛋白的测定。仅供研究使用。不用于诊断程序。 引言 乳铁蛋白&#xff08;LF&#xff09;是一种具有抗菌活性的多…

Spring源码深度解析十六:@Aspect方式的AOP下篇 - createProxy

一、前言 文章目录&#xff1a;Spring源码深度解析&#xff1a;文章目录 我们上篇已经分析到了 Spring将已经找到所有适用于当前bean 的Advisor 集合。下面就要创建代理对象了&#xff0c;而代理对象的创建是从 AbstractAutoProxyCreator#createProxy()开始。下面我们就来看看…

微信小程序|基于小程序+C#制作一个聊天系统

此文主要基于小程序C#使用WebSocket制作一个聊天系统&#xff0c;基本实现小程序与服务端的聊天功能。用小程序自带的客服功能只能绑定微信且一对一沟通&#xff0c;接入市面上成熟的即时通讯预算又略显不足&#xff0c;干脆自己开发一个也能应对简单的业务场景。 实现流程1、服…

数据智仓功能介绍(二)

界面介绍 访问入口 具备权限的人员从系统管理后台进入界面&#xff0c;点击数据智仓&#xff0c;右侧是展示系统中已经配置的数据集。 数据集展示界面 如下图所示&#xff0c;系统中已经配置的数据集包含 数据集名称&#xff0c;修改人员&#xff0c;上次运行时间&#xff08…

matlab智能算法之遗传算法

智能算法之遗传算法智能算法之遗传算法1.背景2.算法3.案例3.1 案例求解二元函数的最大值智能算法之遗传算法 1.背景 2.算法 3.案例 3.1 案例求解二元函数的最大值 例1&#xff1a;计算二元函数f(x,y)20x2y2−10∗(cos(2πx)cos(2πy))f(x,y)20x^2y^2-10*(cos(2\pi x)cos(2…

[附源码]Node.js计算机毕业设计大学生健康系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Qt扫盲-QToolButton 理论总结

QToolButton 理论总结1. 概述2. 使用场景3. 外观样式4. 菜单用途1. 概述 ToolButton 是一种特殊按钮&#xff0c;用于快速访问 特定命令或选项。与普通PushButton 按钮常用显示内容不同&#xff0c;ToolButton 通常不显示文本标签&#xff0c;而是显示图标。当然&#xff0c;也…

Windows与Linux利用系统自带实现共享文件夹的功能

这里需要两台机器在同一局域网或者可互相ping通。系统以Windows11和Windows Subsystem for Linux&#xff08;Ubuntu22.04.1&#xff09;或国产Linux发行版的统信UOS&#xff08;版本号20&#xff09;为例&#xff0c;其他的版本系统也类似&#xff0c;非Linux虚拟机也测试过&a…

蚁群算法详解-解决TSP问题

文章目录前言一、蚁群算法是什么&#xff1f;算法步骤二、基本原理三、数学模型1、算法中的参数设置2、构建路径轮盘赌例子3、更新信息素浓度代码终止四、代码展示五、参数实际设定1.参数设定的准则2.蚂蚁数量3.信息素因子4.启发函数因子5.信息素挥发因子6. 最大迭代次数7. 组合…

Android 实现相机(Camera)预览

CameraX 是一个 Jetpack 库&#xff0c;旨在帮助您更轻松地开发相机应用。 对于新应用&#xff0c;我们建议从 CameraX 开始。它提供一致且易于使用的 API&#xff0c;适用于绝大多数 Android 设备&#xff0c;并向后兼容 Android 5.0&#xff08;API 级别 21&#xff09;。 Ca…

Python基础篇学习

本篇博文目录:一.Python基础语法1.Python基础知识2.了解Python的基础语法结构3.python基础知识二.数据类型1.数字2.字符串3.布尔4.空值&#xff1a;None5.列表6.元祖7.字典8.Bytes9.集合(Set)三:程序三大结构( 顺序结构&#xff0c;分支结构&#xff0c;循环结构)1.顺序结构2.分…

2022-我的秋招之旅

1. 自我介绍 ​ 版1&#xff1a;&#xff08;实习&#xff09; ​ 面试官好&#xff0c;我叫xx&#xff0c;来自xx&#xff0c;目前研究生xx&#xff0c;就读于xx&#xff0c;在研究生期间&#xff0c;担任的职务为xx&#xff0c;在校期间参加各种比赛&#xff0c;如xx等&…