Java调用C/CPP
一、原理
Java调用C/CPP是通过调用C/CPP生成的动态链接库,因此需要保证Java与C的接口一致才能正常调用.可以利用Javah(高版本中为Javac -h)功能生成C接口进行完善C功能即可。最后将C单独编译生成动态链接库由java调用。以下是步骤
1.写Java接口
public class ArraySum {
static {
String path = "/home/jin/Desktop/java_cpp/";
System.load(path+"ArraySum.so");
}
public native static int complexComputition(int ptr[]);
public static void main(String[] args) {
int data[] = new int[100];
for(int i=0;i<data.length;i++)
{
data[i]=i;
}
ArraySum as = new ArraySum();
System.out.println(as.complexComputition(data));
}
}
终端执行javac 编译生成ArraySum.class二进制文件:
jin@jin-PC:~/Desktop/java_cpp$ javac ArraySum.java
jin@jin-PC:~/Desktop/java_cpp$ ls
ArraySum.class ArraySum.java
终端执行javac -h 生成c接口:
jin@jin-PC:~/Desktop/java_cpp$ javac -h . ArraySum.java
jin@jin-PC:~/Desktop/java_cpp$ ls
ArraySum.class ArraySum.h ArraySum.java
2.写C/CPP库文件
生成的ArraySum.h头文件
#include <jni.h>
#ifndef _Included_ArraySum
#define _Included_ArraySum
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_ArraySum_complexComputition
(JNIEnv *, jclass, jintArray);
#ifdef __cplusplus
}
#endif
#endif
自己将源代码补充完整,对应源代码:
#include "ArraySum.h"
#include <iostream>
JNIEXPORT jint JNICALL Java_ArraySum_complexComputition
(JNIEnv *env, jclass, jintArray ptr, jint np){
std::cout<<"Array Sum From CPP"<<std::endl;
jint res=0;
int *p = env->GetIntArrayElements(ptr, nullptr);
jsize len = env->GetArrayLength(ptr);
for(int i=0;i<len;i++)
{
res+=p[i];
}
return res;
}
因为生成的头文件包含了一个jni.h,因此这里需要将java的路径添加到环境变量里面。
CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/home/jin/jdk-15.0.1/include:/home/jin/jdk-15.0.1/include/linux
export CPLUS_INCLUDE_PATH
编译成库文件:
jin@jin-PC:~/Desktop/java_cpp$ ls
ArraySum.class ArraySum.cpp ArraySum.h ArraySum.java
jin@jin-PC:~/Desktop/java_cpp$ g++ ArraySum.cpp --shared -o ArraySum.so -fPIC
jin@jin-PC:~/Desktop/java_cpp$ ls
ArraySum.class ArraySum.cpp ArraySum.h ArraySum.java ArraySum.so
3.执行Java二进制文件
jin@jin-PC:~/Desktop/java_cpp$ java ArraySum
Array Sum From CPP
4950
二、Java调用Python
原理
Python拥有非常多非常好用的开源库。Java可以通过调用Python来对Python的库进行调用。
对Python库调用有两种方法,一种是调用Jython,一种是终端执行Python脚本。
第一种方法Jython在我的理解实际上就是Java语言的Python实现,因此可以直接用Python的语法来写,实际上最终还是运行在JRE中。Java调用Jython之后,实际上还是将Python的语言翻译成了通过Jython翻译成了Java,因此确实做到了Java可以调用Python。虽然Java确实可以做到对Python函数类进行调用,但实际上是调用的Jython,也就是说可以跑Python的代码,但是调的不是真正的Python。目前Jython仅发布了2.7版本,而Python最新版本为3.8,2.7版本支持的库不多,以后也会越来越少。也就是说,调用没有任何三方库依赖的Python是没有问题的,如果Python有三方库依赖,就得看Jython支不支持这个库。因此通过Java对Python进行函数传参形式的调用并不是一个好的策略。
第二种方法是在终端调用Python,改方法只需要在java中单独开辟一个进程去执行Python即可。并没有实现Java调用C那样的传参调用。
Java调用Jython运行对Python函数进行调用
1.想要调用的Python
想要调用以下的python脚本,输出参数,保存出图:
import matplotlib.pyplot as plt
import numpy as np
class PlotSinX(object):
"""docstring for ClassName"""
def __init__(self, begin,end,space):
self.begin=begin
self.end=end
self.space=space
x = np.arange(self.begin,self.end,self.space)
y = np.sin(x)
plt.plot(x,y)
def show(self):
plt.show()
def savefig(self,path):
plt.savefig(path)
if __name__=="__main__":
plotsinx = PlotSinX(0,2*np.pi,0.01)
plotsinx.savefig("./sinx.jpg")
2.Java脚本
给出Java调用Jython脚本:
import org.python.util.PythonInterpreter;
import java.util.Properties;
public class SaveSinX {
public static void main(String[] args) {
String pythonClass = "PlotSinX.py";
String pythonObjName = "plotSinX";
String pythonClazzName = "PlotSinX";
PythonInterpreter pythonInterpreter = new PythonInterpreter();
pythonInterpreter.execfile(pythonClass);
System.out.println( "Hello World!" );
}
}
运行的时候需要安装Jython与Jython的jar包。将Jython的jar包导入项目,即可在里面调用Jython编译器取解析Jython的语法,里面的函数与类,并在Java中实现调用。具体操作过程中,由于很多的Python库Jython2.7已经不在支持,因此没有实现matplotlib出图。
具体可以参考:
1.
Java终端调用Python
虽然Java调用Python并不能像Java调用C/C++一样函数传参等调用那么方便,但是还是可以通过Java在终端对Python进行调用利用到Python的大量开源库。该调用方法是通过Java开启一个新的进程,新进程执行终端命令来调用,参数的传递可以通过字符串传入终端,结果可以从终端打印中截取输出流返回到Java。这会涉及到变量的类型转换,因为无论是输入还是输出,变量类型都是字符串类型的,转换过程会有数值精度损失。另一种参数传递方法是通过文件交互,这可以将数据以二进制的形式从Java输出,在用Python进行读入,也不失为一种好的办法。
1.想要调用的Python
仍然是上一个python脚本,输出参数,保存出图:
import matplotlib.pyplot as plt
import numpy as np
class PlotSinX(object):
"""docstring for ClassName"""
def __init__(self, begin,end,space):
self.begin=begin
self.end=end
self.space=space
x = np.arange(self.begin,self.end,self.space)
y = np.sin(x)
plt.plot(x,y)
def show(self):
plt.show()
def savefig(self,path):
plt.savefig(path)
if __name__=="__main__":
plotsinx = PlotSinX(0,2*np.pi,0.01)
plotsinx.savefig("./sinx.jpg")
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class SaveSinX {
public static void main(String[] args) {
String[] arguments = new String[] { "python3", "/home/jin/Desktop/java_python/PlotSinX.py"};
try {
Process process = Runtime.getRuntime().exec(arguments);
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
int re = process.waitFor();
System.out.println(re);
} catch (Exception e) {
e.printStackTrace();
}
}
}
终端执行,成功调用Python,输出sinx.jpg:
jin@jin-PC:~/Desktop/java_python$ javac SaveSinX.java
jin@jin-PC:~/Desktop/java_python$ java SaveSinX
0
jin@jin-PC:~/Desktop/java_python$ ls
PlotSinX.py SaveSinX.class SaveSinX.java sinx.jpg
jin@jin-PC:~/Desktop/java_python$
三、Java调用Matlab库
原理
Java调用Matlab是通过Matlab开发工具将Matlab代码打包成Jar二进制包与.java格式的接口文件。需要Java版本与Matlab版本一致,若版本不一致有可能会调用失败。
1.写Matlab脚本
Matlab脚本如下,就对一个矩阵进行求逆:
function res = inverseMat(x)
%INVERSE 此处显示有关此函数的摘要
% 此处显示详细说明
res = inv(x);
end
2.打包Jar包
在Matlab命令行中输入
deploytool
打开MatlabCompiler界面,选择Library Complier,进入Library Complier面板。TYPE里面选择Java,并添加函数到EXPORTED FUNCTIONS。下面是将这些函数打包成一个类,对类属性进行一些设置,如类的名字(这里为Inversor)等信息。填好以后,点击package,完成打包。
在Matlab工作目录下,生成一个打包的项目文件与一个文件夹。在里面可以找到打包好的Jar包。
在for_testing文件夹下面可以找到Java文件,我们可以看到里面的接口
public class Inversor extends MWComponentInstance<Inversor>
{
public void inverseMat(List lhs, List rhs) throws MWException;
public void inverseMat(Object[] lhs, Object[] rhs) throws MWException;
public Object[] inverseMat(int nargout, Object... rhs) throws MWException;
}
Matlab的函数被打包到了Inversor Class里面,成为了两个类函数。输入lhs是一个list,输出rhs也是一个Object list。
3.在Java中进行调用
Java代码如下:
import com.mathworks.toolbox.javabuilder.*;
import inverseMat.*;
public class JavaCallMatlab {
public static void main(String[] args) {
MWNumericArray x = null;
Object[] output = null;
int n = 5;
int[] dims = { n, n };
x = MWNumericArray.newInstance(dims, MWClassID.DOUBLE,
MWComplexity.REAL);
for (int i = 1; i <= n; i++) {
for(int j=1;j<=n;j++)
{
x.set( (i-1)*n + j, Math.random());
}
}
Inversor inv = null;
try {
inv = new Inversor();
output = inv.inverseMat( 1, x);
System.out.println("Original Matrix:");
System.out.println(x);
System.out.println("Matrix inversed by Matlab:");
System.out.println(output[0]);
} catch (Exception e) {
System.out.println("Exception: " + e.toString());
}
}
}
Java调用Matlab的Jar包时,所有的变量都要用com.mathworks.toolbox.javabuilder中的变量,来完成计算。最后执行上述程序,输出结果如下:
Original Matrix:
0.3643 0.2529 0.8693 0.9781 0.4976
0.2114 0.1854 0.3979 0.2533 0.0947
0.7052 0.5359 0.7140 0.6467 0.3082
0.6461 0.0348 0.6758 0.4369 0.9079
0.5899 0.2316 0.7723 0.0703 0.0392
Matrix inversed by Matlab:
1.8959 -13.4948 2.4991 -0.6116 3.0433
-4.1840 15.1738 0.2985 0.7852 -4.0712
-0.3377 6.1134 -2.0705 0.2405 0.2291
2.8801 -8.7460 1.2695 -1.1444 1.0918
-2.3238 8.6814 -0.8597 1.8783 -2.7060