JNI是Java Native Interface的缩写,中文为JAVA本地调用。使用JNI可以很方便的用我们的Java程序调用C/C++程序。很多时候,某些功能用Java无法实现,比如说涉及到底层驱动的一些功能,这时候我们就可以利用JNI来调用C或者C++程序来实现,这就是JNI的强大之处。但是JNI也有它的缺点,使用java与本地已编译的代码交互,通常会丧失平台可移植性。

  下面是一个Helloworld例子,调用C++方法输出“Hello World. From JavaAndC++.”

第一步:创建Java类,在里面定义一个本地方法(用native关键字修饰的方法)

java调用c方法 java调用c++程序_本地方法

第二步:使用javah命令(javah  类的全路径)生成本地方法的C++头文件

   打开MainCls.java文件所在目录,打开DOS窗口,输入: javac MainCls.java,回车,生成class文件,MainCls.class

java调用c方法 java调用c++程序_java调用c方法_02

java调用c方法 java调用c++程序_java_03

 在类文件的包目录的起始目录下打开DOS,如:com目录下,

java调用c方法 java调用c++程序_Java_04

输入javah 包.类名,回车,生成.h文件,这个头文件是根据包名和类名来命名的。

java调用c方法 java调用c++程序_本地方法_05

java调用c方法 java调用c++程序_Java_06

这是上面那个文件的内容:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lvshi_javac_test_MainCls */

#ifndef _Included_com_lvshi_javac_test_MainCls
#define _Included_com_lvshi_javac_test_MainCls
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lvshi_javac_test_MainCls
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_lvshi_javac_test_MainCls_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

 JNIEXPORT void JNICALL Java_com_lvshi_javac_test_MainCls_sayHello (JNIEnv *, jobject);

是对 MainCls 类中的本地方法sayHello()的声明,这个h文件相当于我们在java里面的接口,这里声明了一个 Java_com_lvshi_javac_test_MainCls_sayHello(JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。

然后把这个文件拷贝到和.cpp文件同级目录下:

java调用c方法 java调用c++程序_java调用c方法_07

第三步:编写C/C++本地代码,生成动态链接库文件

 使用Visual Studio或者其他的编辑器新建一个C++项目,新建一个.cpp文件(这是C++的源文件)

 (1) 把Java的jdk的include目录下的jni.h文件、win32目录下的jawt_md.h、jni_md.h文件拷贝到Visual Studio的D:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.11.25503\include安装目录下(在其安装的头目录下的搜索框中输入iostream,点击搜索即可看到)

 

java调用c方法 java调用c++程序_java_08

java调用c方法 java调用c++程序_Java_09

拷贝到:

java调用c方法 java调用c++程序_本地方法_10

在.cpp文件中引入这几个库文件:

java调用c方法 java调用c++程序_本地方法_11

按F7编译这个文件,可能会出现以下几个错误,解决方法附上:

<1>#include "com_lvshi_javac_test_MainCls.h" 显示无法打开源文件

1.把com_lvshi_javac_test_MainCls.h文件拷贝到当前编辑文件的同级目录下
2.右键C++项目,属性,C/C++,常规,将该文件的全路径拷贝到“附加包含目录”一栏中,两个路径之间使用分号;隔开

<2>fatal error C1083: 无法打开包括文件:“jawt.h”: No such file or directory

双击错误的地方打开jawt.h文件,把波浪线标红的文件改成#include "jawt_md.h",替换掉原来的文件

<3>LINK : fatal error LNK1561: 必须定义入口点

每个应用程序(文件格式为exe)都应该有个启动点,这个点就是main函数。缺少就会出现link error 。
解决方法有2:
1.添加含有main函数的CPP文件(这里不采用这个方法)
2 右击属性,在常规项中修改配置类型,将应用程序修改为库,动静态库

java调用c方法 java调用c++程序_Java_12

以上按F7编译如果没有错误,就会在C++项目的Debug目录下生成 .dll文件,以 .cpp文件命名

java调用c方法 java调用c++程序_java调用c方法_13

拷贝这个文件到Java的jdk的安装目录的bin目录下,或者为了方便,我们也可以将dll所在的工程目录加入到环境变量path中去,这样可以避免每次都要拷贝的麻烦。注意修改环境变量之后要重启myeclipse

 

java调用c方法 java调用c++程序_java调用c方法_14

第四步:Java调用本地函数

 继续编写Java程序,编写main方法,调用C++的函数方法sayHello()

package com.lvshi.javac.test;

public class MainCls {
	
	// 声明本地方法
	public native void sayHello();
	
	// 调用本地方法
	public static void main(String[] args) {
		/**
		 * 加载本地函数,Demo01这个名称要和生成的Demo01.dll名称一致,
		 * 加载动态链接库,JVM只需要加载一次就可以调用了,
		 * “NativeCode”是上面生成的动态链接库的名字,不含后缀名
		 */
		System.loadLibrary("Demo01");	
		// 创建Java类对象
		MainCls mainCls = new MainCls();
		// 调用本地函数
		mainCls.sayHello();
	}
}

 运行以上的Java程序,如果报以下错误:

Exception in thread "main" java.lang.UnsatisfiedLinkError: 
D:\Program Files\Java\jdk1.7.0_79\bin\Demo01.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

就到Visual Studio中把.cpp文件改成x64系统下的文件(具体看物理机的系统是32位还是64位)

java调用c方法 java调用c++程序_Java_15

按F7重新编译,生成Demo01.dll文件,拷贝这个文件到D:\Program Files\Java\jdk1.7.0_79\bin目录下重新覆盖掉原来的.dll文件,clean一下Java项目,

java调用c方法 java调用c++程序_Java_16

 

再一次执行Java程序,就会打印出结果:

java调用c方法 java调用c++程序_本地方法_17