JNI 是
Java Native Interface 的缩写。 Java™ 本机接口(Java Native Interface,JNI)是一个标准的 Java API,它支持将 Java 代码与使用其他编程语言编写的代码相集成。如果您希望利用已有的代码资源,那么可以使用 JNI 作为您工具包中的关键组件 —— 比如在面向服务架构(SOA)和基于云的系统中。
1、在JDK目录下的
javah.exe就是可以将Java代码转化成C++代码的头文件。(注意完整类名与文件路径)
2、将生成的头文件复制到C++工程下,(注意:JDK目录下的include头文件也需要相应的包含到工程里面), 按照生成的函数声明来写函数实现就可以了。在cpp文件中要include这个文件。
#include <jni.h>这种是在系统目录下去找,自己复制进去的需要改成双引号#include "jni.h"。
3、编译好工程之后,就会生成dll文件了。(dll文件和exe一样, 是可执行的二进制代码)
4、生成dll后,就将其加到环境变量,方便调用。如在Java中
System.loadLibrary("aaa")可调用aaa.dll
Eclipse在每次启动的时候会读取一次环境变量,更改环境变量之后需要重启eclipse。
Java中类型与C/C++中对应关系
Java中的类的对应
Sign签名, 用来识别对应各个方法。
JDK下的
javap.exe能输出签名。用法
javap -s -p 完整类名
全局引用/局部引用/弱全局引用:
弱全局引用不会阻止垃圾回收器回收这个引用所指的对象。因此弱全局引用所引用的对象不一定存在。所以有必要先判断。
Java中多态与C++中的Virtual对应关系(父类与子类的调用):
Java中类的成员方法都是虚拟的。CallNonvirtualVoidMethod()方法来调用。
一个简单的例子:
Java端代码:
package com.jni;
import java.util.Date;
/*
* 2. 本地代码C++访问Java中的类与方法等. 在dll中改变了property的值, 调用了他的方法.
*/
public class NativeAccessJava
{
//本地方法声明
public native void nativeAccessJavaMethod();
//Java中的属性
public int property=10;
//Java中的方法
public int function(int a, Date date, int[] b)
{
System.out.println("Native C code access Java Class!");
return 0;
}
//在C代码中调用这个方法
public double maxValue(double a,double b)
{
return a>b?a:b;
}
//关于多态与C++中virtual函数的对应
public Father inst = new Son();
public static void main(String[] args)
{
NativeAccessJava nativeAccessJava = new NativeAccessJava();
System.out.println("改变前的值是: "+nativeAccessJava.property);
System.setProperty("java.library.path", ".");//这里就是在改临时环境变量
System.loadLibrary("NativeAccessObject");//NativeAccessObject.dll, 为了方便, 可以将动态链接库直接设置到环境变量里面
//改变值的函数是在nativeAccessJavaMethod()中写着的, 所以要先嗲用这个方法之后, 值才改变的
nativeAccessJava.nativeAccessJavaMethod();
System.out.println("改变后的值是: "+nativeAccessJava.property);
}
}
C++端代码:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_jni_NativeAccessJava */
#ifndef _Included_com_jni_NativeAccessJava
#define _Included_com_jni_NativeAccessJava
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jni_NativeAccessJava
* Method: nativeAccessJavaMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jni_NativeAccessJava_nativeAccessJavaMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include "com_jni_NativeAccessJava.h"
#include "windows.h"
#include <iostream>
#include <algorithm>
using namespace std;
/************************************************************************/
/* 2. 本地C代码调用Java中的类. */
/************************************************************************/
JNIEXPORT void JNICALL Java_com_jni_NativeAccessJava_nativeAccessJavaMethod(JNIEnv *env, jobject obj)
{
jclass java_class = env->GetObjectClass(obj);//get class object
jfieldID fieldID_prop = env->GetFieldID(java_class,"property","I");//get property id
jmethodID methodID_func = env->GetMethodID(java_class,"function","(ILjava/util/Date;[I)I");//get method id e.g.
//取得Java类中属性property的值
jint number = env->GetIntField(obj,fieldID_prop);
cout << number<<endl; //输出取得的值, 并输出. 这个在Java调用的时候会输出.
//修改property的值, 改为120
env->SetIntField(obj,fieldID_prop,120L);
//调用java中的方法
jmethodID methodID_max = env->GetMethodID(java_class,"maxValue","(DD)D");//get method ID
jdouble maxValue = env->CallDoubleMethod(obj,methodID_max,3.11,3.15);
cout<< "the maxValue is "<<maxValue<<endl;
//调用java继承关系的类, 调用的是子类的func方法. 相当于Father p = nativeAccessJava.p; p.func();
jfieldID id_inst = env->GetFieldID(java_class,"inst","Lcom/jni/Father;");
jobject inst = env->GetObjectField(obj,id_inst);//get Father class obj
jclass class_Father = env->FindClass("com/jni/Father");//get Father class
jmethodID id_Father_func = env->GetMethodID(class_Father,"func","()V");
cout<<"------------默认是virtual, CallVoidMethod-------------"<<endl;
env->CallVoidMethod(inst,id_Father_func); //调用子类的方法
cout<<"------------CallNonvirtualVoidMethod-------------"<<endl;
env->CallNonvirtualVoidMethod(inst,class_Father,id_Father_func);//调用父类的方法
}
注意:
Java中的int对于C++中的long型,因此要写成100L的形式。
Java中的字符是unicode,占用2个字节,而c++中站一个字节,需要扩宽,因此char就要变成L‘3’型。
关于一些变量的声明等信息,全在jni.h头文件中寻找就行了。
使用JNI的两个弊端:
( 1)、使用JNI后JAVA程序就不能跨平台了.
(2)、Java是强类型的语言,而C++不是,写JNI时必须非常小心。
强类型语言 :
首先我们要声明Java 语言强类型语言的重要性。首先,每个变量有类型,每个表达式有类型,而且每种类型是严格定义的。其次,所有的数值传递,不管是直接的还是通过方法调用经由参数传过去的都要先进行类型相容性的检查。有些语言没有自动强迫进行数据类型相容性的检查或对冲突的类型进行转换的机制。Java 编译器对所有的表达式和参数都要进行类型相容性的检查以保证类型是兼容的。任何类型的不匹配都是错误的,在编译器完成编译以前,错误必须被改正。 如果你有C或C++的背景,一定要记住Java对数据类型兼容性的要求比任何语言都要严格。例如,
在C/C++ 中你能把浮点型值赋给一个整数
。
在Java 中则不能。另外,
C语言中,在一个参数和一个自变量之间没有必然的强制的类型检查。在Java 中则有。起初你可能发现Java 的强制类型检查有点繁烦。但是要记住,从长远来说它将帮助你减少程序出错的可能性。