文章目录

  • 1、native方法介绍
  • 2、Java程序的编写和头文件生成
  • 3、C++函数编写及dll生成
    • 3.1、创建C++工程
    • 3.2、修改编译器设置
    • 3.3、添加头文件
    • 3.4、修改Main.h以及实现其中的函数
    • 3.5、编译生成dll文件并运行Java程序
  • 4、题外话

我们在使用hashmap时,有时候会看到hashcode的源码。最原始的hashcode源码是位于Object类中,是一个native方法。native方法是指,它没有方法体,不是用Java语言实现的。而是通过动态链接库(dll)加载而调用的函数。动态链接库一般使用C、C++、汇编语言等编写,这就实现了和底层函数的交互,类似于API的功能。我们今天就来实现一个native方法。当然,操作系统是windows10,64位。Linux也类似,只不过生成的动态链接库是so文件。

1、native方法介绍

Java虽然功能强大,但是存在一个缺点,就是JVM本身也是一个动态链接库(内部有class文件的解释器),它加载类和解释执行的效率不如直接编译的C++高。再有就是,Java设计系统API等底层操作时可能无能为力。一些经常调用的函数,或者和操作系统交互的函数必须用其他语言来完成。

Java中的JNI(Java Native Interface)就是实现native方法的途径。它通过C/C++的编程接口(头文件)来达到和C/C++交互的目的。

我们先来看一下,native方法的执行过程。

首先,在类被加载时,需要加载native方法实现的动态链接库,因此这段加载代码必须是静态加载器中的程序段。比如下面的ExampleClass类:

public class ExampleClass {static {System.load("example.dll");}public native void example();
}

然后,当JVM执行到native函数时,查找已经加载好的动态链接库,如果找到对应函数的实现,则把执行权转交操作系统,操作系统将进程调度至动态链接库,开始函数执行,Java程序则等待其返回值;如果未找到则报错(属于Error型异常,也就是JVM级别的异常,不可捕获)。

了解到这一点,我们就可以编写自己的native方法了。程序员出生时说的第一句话(滑稽)是Hello World,所以我们也以Hello World为例。环境如下:

操作系统:windows 10 64bit

Java IDE:IDEA 2018.3–64bit(eclipse的小伙伴自行对应)

JDK:1.8(AMD64)

C++ IDE:codeblocks 17

C++ compiler:mingw64-g++-8.1(注意,codeblocks自带的编译器是32位的,生成的动态链接库也是32位的,不能被64位的JVM加载。所以稍后我们将手动修改codeblocks的编译器路径)

2、Java程序的编写和头文件生成

我们首先新建Java工程,编写一个简单的Java程序,直接在主类中就行。如下:

public class Main {static {System.load("D:\\jni.dll");}public native static void hello(); // 必须有static,因为静态函数不能直接调用同一个类的非静态函数public static void main(String[] args) {hello();}
}

这个hello函数就是我们要实现的native函数,功能是输出字符串“Hello World”。在这个主类中,需要加载D:\jni.dll,所以把它写在static初始化器中。

接下来,我们需要编译出class文件,并生成一个C++的头(.h)文件。我们单击idea的一键编译运行即可。这次运行是一定会报错的,提示无法加载动态链接库。但我们不需要运行,只需要那个class文件。

找到你的class文件所在位置。比如,我的是在工程目录\out\production\doorsymbol下,其中doorsymbol是我的工程名。eclipse的小伙伴自行找。反正看到Main.class就对了。如下图:


然后,打开命令行,转到当前目录下。输入命令:

javah -jni Main

之后便会看到一个Main.h文件。这一步算大功告成。

3、C++函数编写及dll生成

3.1、创建C++工程

这一步我们将生成dll文件。我们用codeblocks新建一个C++工程。注意,这次不是控制台程序,而是动态链接库了。工程目录不要有空格,比如我选择D:\cppprojects\jni

这一步一定要选这个:

3.2、修改编译器设置

刚才提到,codeblocks自带的编译器是mingw32,无法编译64位的动态链接库。所以我们需要把默认编译器路径改成mingw64所在的路径。没有mingw64的小伙伴可以下载这里的压缩包并解压。链接:https://pan.baidu.com/s/1jsxoCnntqCo4xRBIFF1ACw
提取码:tian

解压后,转到mingw64文件夹中。如图:

我们只需要把编译器路径改成这个文件夹路径即可。

codeblocks–settings–compiler

点可执行工具链(executable tool chain)选项卡,把编译器的安装目录改成刚才的mingw64文件夹。

接着,修改编译选项。单击compiler settings选项卡。选择遵循c++11规范,以及生成x86_64目标。尤其是生成64位目标至关重要,这将直接决定我们生成动态链接库的格式。


单击确定。

然后再修改调试器路径,也就是gdb。如下图:

settings–debugger settings

单击default选项卡,把调试器路径(executable path)改成刚才的mingw64文件夹下的bin/gdb.exe即可。单击确定。

3.3、添加头文件

编译器设置好之后,我们的工程中,有一个默认的main.cpp文件和main.h文件。其中main.h文件没有任何用处,把它删掉。我们需要用的是刚才用class文件生成的Main.h。打开工程目录,把这个头文件移动到和main.cpp同级目录下。然后,打开你的jdk路径(以下简称java_home),如下图:

把以下两个头文件放入和main.cpp同级的目录下:

%java_home%\include\jni.h
%java_home%\include\win32\jni_md.h

最终的效果如下图

3.4、修改Main.h以及实现其中的函数

打开Main.h,发现它声明了一个函数Java_Main_hello。这个函数就是Java中native函数hello的真正接口。另外,我们看到头文件包含了#include <jni.h>,我们把它改成#include "jni.h"

然后打开main.cpp文件,删除全部内容,改成如下:

#include "Main.h"#include <iostream>using namespace std;JNIEXPORT
void JNICALL Java_Main_hello(JNIEnv *, jclass)
{cout << "Hello World!" << endl;
}

注意一定要包含头文件Main.h,并且函数头必须与Main.h中的声明完全相同。为避免出错,建议复制粘贴。

3.5、编译生成dll文件并运行Java程序

这一切都完毕之后,单击编译按钮,如果没有出错,则生成动态链接库成功。我们到工程目录下bin\Debug中,找到一个dll文件。把它复制到D盘根目录下,并改名为jni.dll


返回IDEA,再次运行Java程序,可见输出Hello World!字样。这样,我们的native函数就圆满成功了。

4、题外话

思考:如果一个类有多个native函数是什么情况?如果有多个含native函数的类,又是什么情况?

答案:一个类多个native,只生成一个头文件,但头文件中有多个函数声明。多个含native的类,则每个类分别生成一个头文件。

Java之native函数相关推荐

  1. java native方法_并发系列-native函数回调Java方法原理实践

    写在前面 上一篇分享了Java调用native函数过程原理实践,文章最后留了一个问题,本章主要对C程序回调我们的Java程序原理进行实践. 调用C程序之后他是怎么知道来调用我们我们的哪个方法?又是如何 ...

  2. java native函数库_Java 层调用 Native 层函数的两种方式

    概述 Java 层如何调用Native层函数,大家都应该知道使用JNI(Java 本地接口). 通过在java层声明native方法,然后遵守JNI规范命名Native函数,即可建立Java层nati ...

  3. java中原生方法_java中原生(native)函数的用法

    1.原生(Native)函数的概念 在Java程序中,可以使用由其他编程语言实现的函数,这种函数,在Java中被称之为 原生(Native)函数. 2.在Java程序中使用原生函数的优缺点 1)优点 ...

  4. java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数

    打算对Android的NDK的开发做一总结,首先是JNI部分,接下来是NDK的内容.今天首先介绍一下JNI的第一部分:注册native函数. 当java代码中执行native的代码时候,首先是通过一定 ...

  5. 深入了解android平台的jni---注册native函数

    注册native函数有两种方法:静态注册和动态注册. 1.静态注册方法 根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果没有就会报错,如果存在则会建立一个关联联 ...

  6. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | native 函数查询 | dalvik_system_DexFile.cpp#defineClassNative 函数 )

    文章目录 前言 一.查询 defineClassNative 函数 二.dalvik_system_DexFile.cpp#Dalvik_dalvik_system_DexFile_defineCla ...

  7. 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )

    文章目录 一.DexFile 构造函数 二.DexFile.openInMemoryDexFile 函数 三.Android 源码中查找 native 函数 一.DexFile 构造函数 上一篇博客 ...

  8. java基础提升篇:Java中Native关键字的作用

    初遇 初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法: public native int hashCode(); 为什么有个native呢?这是我所要 ...

  9. JNI通过线程c回调java层的函数

    1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...

  10. java层 native层_Java层的ServiceManager和Native层的ServiceManager的对应过程

    转自:https://blog.csdn.net/moonshine2016/article/details/54378358 参考:https://www.jianshu.com/p/9c02370 ...

最新文章

  1. 002_SpringBoot整合Servlet
  2. Codeforces 1276C/1277F/1259F Beautiful Rectangle (构造)
  3. [BJOI2019]送别——非旋转treap
  4. selenium (二)
  5. 太阳升起并下落的小动画-SWIFT
  6. matlab 中的矩阵分解
  7. Vue 爬坑之路(四)—— 与 Vuex 的第一次接触
  8. mybatis多参数传递(其中包括数组)
  9. 这几个5.20表白代码发给你女神,还没有女朋友直接来找我!
  10. word转换html分页,将网页(HTML)内容复制转贴到Word的分页控制
  11. 在我差点崩溃了的时候,还好有主从复制
  12. 遥感影像镶嵌及实现(四)
  13. Qt入门极简教程(二)
  14. 计算机课翻译成英语,计算机课程英文翻译
  15. 【ML】单分量Metropolis-Hastings算法与Gibbs抽样
  16. Oracle enq: TX contention 和 enq: TM contention 等待事件说明
  17. PostgreSQL索引膨胀
  18. VScode中的神仙插件(写代码必备)
  19. 软考网络工程师下午考试知识点整理
  20. 【调剂】中国矿业大学接收调剂研究生,资源与环境矿业工程

热门文章

  1. 笔记本计算机拆开视频,神州笔记本拆卸全过程图解
  2. 计算机办公操作excel,办公中常用的Word及Excel的方法有哪些
  3. 《UnityAPI.Camera摄像机》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+allCameras+cullingMask+OnPreCull+立钻哥哥++OK+)
  4. python桌面快捷方式不见了怎么办_桌面快捷方式不见了怎么办?桌面快捷方式不见了解决方法...
  5. 802.11协议总结
  6. 零基础学python这本书怎么样-怎样学 Python?
  7. 织梦后台界面修改方法
  8. 笔记本vmware利用无线网卡上网设置
  9. JAVA支付宝app端支付以及提现
  10. 对论文中模型进行编程实现时的注意要求和总结