Java中调用由C/C++实现的本地库(JNI本地程序调用)

news2024/9/26 1:25:06

文章目录

  • 背景
  • 介绍
    • 什么是JNI?
    • 什么是本地库?
    • 开发Java使用JNI本地库步骤
  • 编写Java类实现JNI本地调用
    • windows系统下编译动态链接库
      • 创建Java项目(demo)
        • 第一步:编写带有native的Java类
        • 第二步:javac生成`NativeDemo`类的字节码文件
        • 第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)
      • 创建C++动态链接库(DLL)
        • 第一步:打开Microsoft Visual Studio 2022创建项目
        • 第二步:配置项目并创建
        • 第三步:加载javah生成的NativeDemo.h头文件
        • 第四步:配置jni.h导入找不到问题
        • 第五步:编辑源码,导入`NativeDemo.h`头,实现对应方法。
        • 第六步:执行本地windows调试器,生成.dll动态库文件
      • 创建Java程序入口类,加载动态库并运行
        • 第一步:创建Main.java文件,加载动态库,调用方法
        • 第二步:编译Main.java、运行Main
    • Linux系统下编译动态链接库
  • 附录
    • javac参数help
    • 出现的一些问题
      • 关于为什么在`javac`编译的时候要指定`-encoding`选项?
      • 关于为什么要把NaviveDemo.h头文件复制到根目录后加载?
    • 参考链接

背景

        最近学习了Java中调用本地程序(JNI:Java Native Interface)、多种语言混合编程。但是关于Java怎么使用JNI调用本地程序是一点儿都不清楚,网上查阅了很多资料,编写了一个demo做简单调用机制的了解。

        jdk自身在开发中也会调用自己的动态库(windows下的.dll文件),如下图:
1710482988613_B6C76C13-E923-4ee3-8E3F-8B3A78E99127.png

介绍

什么是JNI?

    JNI全称叫Java Navtie Interface,中文翻译本地调用。

C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发.
C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程.
以上转载自:https://www.cnblogs.com/jaejaking/p/6840530.html(在Java中调用C/C++本地库)

什么是本地库?

本地库被分为静态库(.a和.lib结尾)动态库(.dll和.so结尾)

  1. 在windows系统下动态库以.dll结尾
  2. 在linux系统下动态库以.so结尾

开发Java使用JNI本地库步骤

  1. 编写Java类,方法要带有native关键字
  2. javac编译Java类,生成class文件
  3. javah生成native对应的头文件(头文件.h)
  4. C/C++实现javah生成的头文件,编译生成.dll动态库
  5. 动态库复制到java类的运行目录下,加载本地库后调用方法、运行

编写Java类实现JNI本地调用

windows系统下编译动态链接库

  • 开发环境:
    1. windows 11系统
    2. jdk8
    3. Visual Studio Code IDE,编写Java代码
    4. Microsoft Visual Studio Community 2022 (64 位),编写C++代码,生成dll动态库
  • 项目结构:

image.png

JNI_TEST
  --java-src   # java源码文件夹 & .dll动态库
  --jni_dll    # c++源码文件夹 & 生成的目标文件(.dll)

创建Java项目(demo)

第一步:编写带有native的Java类
/**
 * Java 本地native方法,具体由C++实现
 */
public class NativeDemo {
    /**
     * 由C++返回一个字符串,通过Java打印出来
     */
    public native String returnString();

}
第二步:javac生成NativeDemo类的字节码文件

打开命令提示符窗口,编译NativeDemo.java文件,生成字节码文件。

image.png

第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)

image.png
        生成的NativeDemo.h,就需要#include 'NativeDemo.h'导入,通过C++去实现了。

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

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

创建C++动态链接库(DLL)

第一步:打开Microsoft Visual Studio 2022创建项目

搜索dll,创建动态链接库。

image.png

第二步:配置项目并创建

image.png

第三步:加载javah生成的NativeDemo.h头文件

        把javah生成的NativeDemo.h头文件复制到jni_dll文件夹的根目录下。加载生成的头文件。

image.png
image.png

第四步:配置jni.h导入找不到问题

        打开加载的NativeDemo.h头文件后,发现jni.hJNIEXPORTJNICALL加载不到,需要对项目进行配置,导入jdk相关到项目,才能使用jni.h。

image.png
image.png
选择完成,应用后,导入的jni.h头文件不会报错了。

第五步:编辑源码,导入NativeDemo.h头,实现对应方法。

打开pch.cpp源码文件,编辑。

image.png

// pch.cpp: 与预编译标头对应的源文件

#include "NativeDemo.h"
#include "pch.h"
#include <iostream>

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 * 复制NativeDemo.h头中定义的方法,c++去实现
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
(JNIEnv* env, jobject jobj) {

	return env -> NewStringUTF("exec dll: this is return from c++");
}

第六步:执行本地windows调试器,生成.dll动态库文件

image.png
        在jni_dll项目的根路径下,x64/Debug/jni_dll.dll就是生成的目标文件,后续被调用的本地动态库也是这个文件。

        到此,创建并生成.dll动态链接库已经完成。

创建Java程序入口类,加载动态库并运行

第一步:创建Main.java文件,加载动态库,调用方法

/**
 * 程序运行入口
 */
public class Main {

    static {
        /**
         * 加载动态库:只需要加载名字就可以,不需要带上.dll后缀,动态库放在和Main运行文件同一个目录下.
         * window上的动态库以.dll结尾,linux动态库以.so结尾
         */
        System.loadLibrary("jni_dll");
    }

    public static void main(String[] args) {
        // 创建Java对象,直接调用方法,具体的实现交给C++底层处理
        NativeDemo demo = new NativeDemo();
        // 调用方法, 由动态库制定并返回
        System.out.println(demo.returnString());

    }
}
第二步:编译Main.java、运行Main

        复制刚生成的jni_dll.dll动态库到可运行文件所在目录,动态库会在此目录下加载。

image.png

Linux系统下编译动态链接库

待补充…

附录

javac参数help

image.png

出现的一些问题

关于为什么在javac编译的时候要指定-encoding选项?

        大多数ide默认的编码环境是utf-8,windows命令提示符窗口,默认是GBK编码。如果使用javac命令直接编译,会出现字符不兼容的错误。所以在javac编译的时候指定使用utf-8的方式进行。

image.png
        可以使用chcp命令查看当前命令提示符窗口的编码方式。

image.png
        修改编码方式后编译同样失败,可能的原因是当前窗口的编码对编译并不生效。
        查阅了相关资料,如果直接对语言和区域的格式进行调整,可能会造成在dos窗口下导致其他字符显示错误。
image.png

关于为什么要把NaviveDemo.h头文件复制到根目录后加载?

        在Microsoft Visual Studio 2022版本编辑器下,加载的头文件不会直接导入项目,在使用#include 'NativeDemo.h'导入头文件的时候,还是会出现找不到头文件的情况。

参考链接

  1. 在Java中调用C/C++本地库
  2. Windows: 转换cmd窗口的默认字符编码 - 夜行过客 - 博客园
  3. windows下vscode+vs2019开发JNI_vscode java jni 的 java.library.path-CSDN博客
  4. VS2019中C++,#include无法打开自己所写的头文件(.h)_vs2019 include报错-CSDN博客
  5. 静态库与动态库的区别与优缺点_静态库和动态库的优缺点-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1539100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++的缺省参数,函数重载,引用

目录 1、缺省参数&#xff08;不能在函数声明和定义中同时出现&#xff0c;若声明和定义是分开的&#xff0c;则缺省参数放在声明里面&#xff09; 1.1、缺省参数的概念 1.2、全缺省 1.3、半缺省 2、函数重载 2.1、特殊情况 2.2、特殊情况 2.3、为什么C支持函数重载而C语…

springboot+itextpdf+thymeleaf+ognl根据静态模版文件实现动态生成pdf文件并导出demo

第一步&#xff1a;导入maven依赖 <!-- 导出为PDF依赖包 --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId></dependency><dependency><groupId>com.itextpdf</groupId><art…

网络安全慢速攻击

什么是低速缓慢攻击&#xff1f; 低速缓慢攻击是 DoS 或 DDoS 攻击的一种&#xff0c;依赖一小串非常慢的流量&#xff0c;可以针对应用程序或服务器资源发起攻击。与更传统的蛮力攻击不同&#xff0c;低速缓慢攻击所需的带宽非常小&#xff0c;并且难以防护&#xff0c;因为它…

Day60:WEB攻防-PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改

目录 PHP-DEMO1-序列化和反序列化 序列化操作 - 即类型转换 序列化案例 PHP-DEMO2-魔术方法触发规则 __construct(): //当对象new的时候会自动调用 __destruct()&#xff1a;//当对象被销毁时会被自动调用 __sleep(): //serialize()执行时被自动调用 __wakeup(): //uns…

程序员表白

啥&#xff1f;&#xff01;你说程序员老实&#xff0c;认真工作&#xff0c;根本不会什么表白&#xff01;那你就错了&#xff01;(除了我) 那今天我们就来讲一下这几个代码&#xff01;赶紧复制下来&#xff0c;这些代码肯定有你有用的时候&#xff01; 1.Python爱心代码 im…

MNN 执行推理(九)

系列文章目录 MNN createFromBuffer&#xff08;一&#xff09; MNN createRuntime&#xff08;二&#xff09; MNN createSession 之 Schedule&#xff08;三&#xff09; MNN createSession 之创建流水线后端&#xff08;四&#xff09; MNN Session 之维度计算&#xff08;五…

墨菲安全在软件供应链安全领域阶段性总结及思考

向外看&#xff1a;墨菲安全在软件供应链安全领域的一些洞察、思考、行动 洞察 现状&挑战&#xff1a; 过去开发安全体系是无法解决软件供应链安全问题的&#xff1b;一些过去专注开发安全领域的厂商正在错误的引导行业用开发安全思维解决软件供应链安全问题&#xff0c;治…

Linux:详解https协议

文章目录 什么是https协议信息窃取常见的加密数据摘要和数据指纹https的工作过程只使用对称加密只使用非对称加密都使用非对称加密非对称加密对称加密 证书数据签名https方案 本篇要总结的内容是关于https协议的相关内容 什么是https协议 在讲述https协议之前&#xff0c;首先…

Linux镜像文件下载地址--SCAS 开源镜像站,速度快

SCAS 开源镜像站 https://mirror.iscas.ac.cn/举例&#xff1a; 下载centos7 Index of /centos/7/isos/x86_64/ (iscas.ac.cn)

【C++算法】二分算法、二分模板详解,四道例题带详细注释

文章目录 [toc]1&#xff09;整数二分2&#xff09;解二分题步骤AcWing 789.数的范围洛谷 P1873.EKO/砍树洛谷 P1678.烦恼的高考志愿 2&#xff09;浮点二分AcWing 790. 数的三次方根 1&#xff09;整数二分 有单调性的题目一定可以二分&#xff0c;但是用二分做的题目不一定拥…

Linux初学(八)磁盘管理

一、磁盘管理 1.1 简介 磁盘的工作原理&#xff1a; 添加磁盘对磁盘进行分区格式化磁盘挂载和使用磁盘 磁盘的类型&#xff1a; 固态机械 磁盘的接口类型&#xff1a; IDESTSTSCSI 磁盘工作的原理&#xff1a; 磁盘&#xff0c;特别是硬盘&#xff0c;和内存不同&#xff0c;…

【Bug】记录2024年遇到的Bug以及修复方案

--------------------------------------------------------分割线 2024.3.22------------------------------------------------------- 1、load_sample_image raise AttributeError(“Cannot find sample image: %s” % image_name) AttributeError: Cannot find sample ima…

nvidia显卡如何安装cuda驱动

目录 查看显卡对应的cuda版本下载与你显卡匹配的CUDA Toolkit 查看显卡对应的cuda版本 按 微软 R 键&#xff0c;输入cmd 然后输入 nvidia-smi &#xff0c;回车显示下面信息&#xff1a; 看到 CUDA Version 为 12.2 下载与你显卡匹配的CUDA Toolkit 打开网页&#xff1a…

鸿蒙Harmony应用开发—ArkTS-@AnimatableExtend装饰器:定义可动画属性

AnimatableExtend装饰器用于自定义可动画的属性方法&#xff0c;在这个属性方法中修改组件不可动画的属性。在动画执行过程时&#xff0c;通过逐帧回调函数修改不可动画属性值&#xff0c;让不可动画属性也能实现动画效果。 可动画属性&#xff1a;如果一个属性方法在animation…

C++默认构造函数(二)

目录 构造函数补充 构造函数初始化列表的使用 赋值运算符重载函数 运算符重载函数介绍 运算符重载函数的使用 赋值运算符重载函数 赋值运算符重载函数的使用 拷贝构造函数和赋值运算符重载函数 重载前置和后置 前置 后置 重载流插入<<与流提取>> 流插…

C++ 其它

1、内存四区-代码区 2、内存四区-全局区 生成exe后&#xff0c;运行前是代码区和全局区 3、内存四区-栈区 4、内存四区-堆区 5、new *new一个整型10&#xff0c;返回的是该数据类型的指针&#xff0c;所以用int p 所以是int [10]&#xff0c;所以new的是int[10]&#x…

3.23项目:聊天室

1、 基于UDP的网络聊天室 项目需求&#xff1a; 如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息如果有人下线&#xff0c;其他用户可以收到这个人的下线信息服务器可以发送系统信息 服务器 #inc…

东方博宜 1469. 数的统计

东方博宜 1469. 数的统计 #include<iostream> using namespace std; int main() {int n ;cin >> n ;int x ;cin >> x ;int cnt ;cnt 0;for (int i 1 ; i < n ; i){int num ;num i ;while(num!0){int g ;g num % 10 ;if (g x)cnt 1 ;num num / 10…

springboot294基于java的火车票订票系统的设计与实现

火车票订票系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装火车票订票系统软件来发挥其高效…

ElasticSearch首次启动忘记密码,更改密码(Windows 10)

先启动ElasticSearch 启动方式cmd到lasticsearch-8.12.2\bin目录下输入elasticsearch 启动成功后新开一个窗口输入elasticsearch-reset-password -u elastic