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++目录下。