1、写一个Java类,生成.class文件
①java源码
package com; public class demo { public demo(){ //Constructor super(); } public static int add(int a,int b){ return a+b; } public boolean judge(boolean bool){ return !bool; } }②包名是com,切换到文件目录(javac demo.java),生成字节码文件
③小插曲:编译一直出错,代码没有问题,我用的notepad++打开的,编码问题(修改为utf-8无BOM格式编码)
④现在com文件下有两个文件(一个java文件,一个class文件)
2、用VS2013写C++代码
①加载jni.h头文件
属性-->配置属性--->VC++目录--->包含目录----》(D:\java\jdk\include)
这里注意:include文件夹下有子文件夹,把子文件夹中的头文件都拷贝一份放在最外层
②源码(中间有一个地方需要修改,自己的jvm.dll文件路径,VS建立的工程是空项目,源文件也是自己新建)
#include<windows.h> #include <jni.h> #include<iostream> using namespace std; int main() { JavaVMOption options[1]; JNIEnv *env; JavaVM *jvm; JavaVMInitArgs vm_args; long status; jclass cls; jmethodID mid; jint square; jboolean not; jobject jobj; cout << "begin" << endl; options[0].optionString = "-Djava.class.path=."; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; typedef jint(WINAPI *PFunCreateJavaVM)(JavaVM **, void **, void *); const char szJvmPath[] = "D://java//jdk//jre//bin//server//jvm.dll"; HINSTANCE hInstance = ::LoadLibrary(szJvmPath); if (hInstance == NULL) { cout << "加载链接库失败" << endl; cout << ::GetLastError() << endl; cout << hInstance << endl; int x;cin >> x; //让程序暂停 return -2; } //取得里面的JNI_CreateJavaVM函数指针 PFunCreateJavaVM funCreateJavaVM = (PFunCreateJavaVM)::GetProcAddress(hInstance, "JNI_CreateJavaVM"); status = (*funCreateJavaVM)(&jvm, (void**)&env, &vm_args); if (status != JNI_ERR) { cout << "JVM虚拟机创建成功" << endl; cls = env->FindClass("com/demo"); if (cls != 0) { cout << "自定义类加载成功" << endl; mid = env->GetStaticMethodID(cls, "add", "(II)I"); if (mid != 0) { square = env->CallStaticIntMethod(cls, mid, 5, 5); std::cout << "ans=" << square << std::endl; } mid = env->GetMethodID(cls, "<init>", "()V"); if (mid != 0) { jobj = env->NewObject(cls, mid); std::cout << "init ok" << std::endl; } mid = env->GetMethodID(cls, "judge", "(Z)Z"); if (mid != 0) { not = env->CallBooleanMethod(jobj, mid, 1); if (!not) { std::cout << "Boolean ok" << std::endl; } } } jvm->DestroyJavaVM(); } else return -1; cout << "end" << endl; int x;cin >> x; //让程序暂停 }③竟然出错了(修改编码方式)
修改编码方式:属性-->配置--->常规--->字符集(改成多字符集)
④链接库加载失败
平台不兼容的问题,VS默认是win32,修改成X64即可。运行会报错找不到一个dll文件
解决:属性-->配置-->链接器--->输入---->附加依赖项(不要从父级继承,取消复选框)
⑤链接库加载成功了,虚拟机也创建成功了,但是加载自定义类出错了(默认路径是环境变量下的,用点代替,可以导入java/lang,要想导入自定义类需要修改路径)
修改加载类的路径:(该路径为.class所在的路径,路径下有com文件夹,文件夹下有.class文件)
options[0].optionString = "-Djava.class.path=C://Users//HQH//Desktop//C++调用Java";⑤运行成功,注意源码中的传参和参数获取(II)I表示两个int类型输入返回类型int(以及区别不同的返回类型调用函数不一致)
3、如果传参和返回值都是String类型怎么办?
①C++/java和jni的数据类型不一致,需要类型转换
//传参,字符串转换jstring jstring stringTojni(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); jstring rstStr = (jstring)env->NewObject(strClass, ctorID, bytes, encoding); return rstStr; } //接收返回值,jstring转换成字符串 char* jniTostring(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; }②调用(现在知道"-Djava.class.path=."路径的用处了吧,就是jdk路径中的一些包)
//调用 if (cls != 0) { cout << "自定义类加载成功" << endl; mid = env->GetStaticMethodID(cls, "fun", "(Ljava/lang/String;)Ljava/lang/String;"); //(传参类型)返回值类型 if (mid != 0) { string s = "C:\\Users\\HQH\\Desktop\\1.jpg"; jstring a = (jstring)env->CallObjectMethod(cls, mid, stringTojni(env, s.c_str())); //string转换char cout << a << endl; char * aa = jniTostring(env, a); cout << aa << endl; cin >> x; //让程序暂停 } }
4、普通java代码可以,那么java程序中若调用第三方jar包怎么办?(多添加一个包的路径即可)
options[0].optionString = "-Djava.class.path=自定义类路径;C://Users//HQH//Desktop//aiyouwei//demo//bin//com//libtensorflow-1.6.0.jar";5、我是利用C++调java,java中使用了TensorFlow的Jar包。那么java环境如何配置TensorFlow接口
①引入刚刚的jar包
②项目右键:Build Path--->Configure--->Libraries--->JRE System--->Native libary location(添加dll文件所在的文件夹 tensorflow_jni.dll)
6、不报错,但是程序没有预期结果,怎么办?
解决办法:把相关的dll全部copy到c++工程项目下(上一步加载的dll文件)
总结:
①C++调用java,需要用到jni,数据类型转换
②C++调用java,-Djava.class.path的配置
③java中路径最好不要设置成相对路径,在被调用时,相对路径是在C++项目下的。若想用需要把用到的文件copy到C++目录下。