record what I learned.
make knowledge simple.
今天学习了下java调用c++,网上资料中windows版的占了大部份,所以写写我在linux 下的学习结果,其中还是有挺多坑的,而且实际是调用C,能调用c++是因为,c++代码都有extern “C”声明。至于为啥研究java调用c++,主要是因为大数据平台是java的嘛。
JNI:写好java接口,然后用c++实现。缺点是必须为了适应java写c++,c++中会出现很多java的定义和方法,java开发方和c++开发放要互相配合。
JNA:依据已有的动态链接库.so文件写java代码调用.so。优点是,写c++代码时不用针对java做适应,c++开发人员完全可以不在乎java问题,java调用c++的所有任务都由java开发人员处理。
————————————-JNI————————
JNI是java标准库,不用额外下载。Jni使用相对麻烦,需要处理很多类型变换问题。
Test.java
import java.lang.*;
import java.io.*;
class Test
{
static
{
//linux下这里不要加路径和后缀
System.loadLibrary("sumImpl");
}
//native 标记, c++代码要实现的
private native void sum(int x, int y);
public static void main(String agr[])
{
try{
Test a = new Test();
while(true)
{
a.sum(1,2);
System.out.println("haha");
Thread.sleep(1000);
}
}catch(Exception e)
{
;
}
}
}
先编译java代码,然后生成c++头文件。
Javac test.java ->生成test.class
Javah test ->生成test.h
Test.h内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: sum
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_Test_sum
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
依据这个头文件实现c++代码即可
sumImpl.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include "Test.h"
using namespace std;
jobject getInstance(JNIEnv* env, jclass obj_class);
JNIEXPORT void JNICALL Java_Test_sum
(JNIEnv *, jobject, jint a, jint b)
{
cout << "test!!" << endl;
}
编译c++代码,此处有个非常大的坑,代码中库名字使用的是sumImpl,如下
System.loadLibrary(“sumImpl”);但是编译时要改名为libsumImpl.so,不然放在哪都找不到!此外,要将jdk/include中的jni.h和jni_md.h加入c++路径中。
g++ -fPIC -shared -o libsumImpl.so sumimpl.cpp
最后执行java即可
Java Test
在依据javah生成头文件写c++时要处理很多类型转换,可以查看jni.h,环境依赖比较大,需要调整的外围参数也比较多。
————————————–JNA————————–
资料说Jna使用的是java 的反射机制,既然是反射机制那就比较好理解了,目前具体的细节没有深究。 先准备代码,一份java,两份c++
Java文件一份
jnaT.java
import com.sun.jna.*;
public class jnaT
{
public interface SU extends Library
{
//这里是加载so文件,这里有个坑,见下面说明,然后定义对应的函数接口
SU Instance = (SU)Native.loadLibrary("sum.so",SU.class);
int sum(int a, int b);
}
//这里只是简单的调用
public static void main(String aa[])
{
try
{
int k = 0;
while(true)
{
k = SU.Instance.sum(1,3);
System.out.println("hah:"+k);
Thread.sleep(1000);
}
}catch(Exception e)
{
;
}
}
}
注意:(此处引用网上总结)
1,Native.loadLibrary()函数有2个参数:
C++代码,一个头文件,一个实现文件
这里参考一份资料,extern “C”的作用,至于为何要使用C规范,我目前没有研究,我初步觉得可能是我研究的这部分只是java调用C++的函数,调用C++的类要使用其他方式。(如果有时间和业务需求会继续研究)
http://baike.baidu.com/link?url=vWkSN0ktPl_eQ-Irow8-0UnFcKhPu-owWadgRU_Ka6Unp8BlhtBs0STcqqvdpnUShUxq2TJ-LmsQNMoLMELkN_
Sum.h
#ifndef _TEST_SUM
#define _TEST_SUM
extern "C" //这部分必须有哈,声明接口
{
int sum(int a,int b);
typedef int myadd_t(int,int);
}
#endif
Sum.cpp
#include "sum.h"
extern "C"
int sum(int a,int b)
{
return a+b;
}
先编译c++,生成so文件
g++ -shared -fPIC -o sum.so sum.cpp
然后编译和运行,这里要指定jna的jar包,jna是第三方包,要自己下载
javac -cp .:./jna-4.0.0.jar jnaT.java
java -cp .:./jna-4.0.0.jar jnaT
这个方法的依赖比较少,只需要写好java适配就好,比较简单。
JNA的不足 JNA是建立在JNI技术基础之上的一个框架。使用JNI技术,不仅可以实现Java访问C函数,也可以实现C语言调用Java代码。而JNA只能实现Java访问C函数,作为一个Java框架,自然不能实现C语言调用Java代码。此时,你还是需要使用JNI技术。