Mohosojo

Logo

record what I learned.
make knowledge simple.

View My GitHub Profile

今天学习了下java调用c++,网上资料中windows版的占了大部份,所以写写我在linux 下的学习结果,其中还是有挺多坑的,而且实际是调用C,能调用c++是因为,c++代码都有extern “C”声明。至于为啥研究java调用c++,主要是因为大数据平台是java的嘛。

 

首先来个对比哈,相信大家会更关注JNA。

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技术。