Java本地方法调用

JNI介绍

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java 1.1 开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。

JNI的使用场景

  1. Java应用需要与本地其它非Java应用交互
  2. 遇到性能瓶颈,通常本地代码执行效率要优于Java代码
  3. JDK提供的功能不足以帮我们实现需要的功能

JNI的问题

  1. 因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了。
  2. JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。
  3. 因为C++和C这样的语言非常灵活,一不小心就容易出错,比如代码就没有写析构字符串释放内存,就存在内存泄漏的问题,对于java developer来说因为有了GC 垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性

JNI实战

主要分为以下几个步骤

  • 编写带有native声明的方法的java类

  • 使用javac命令编译所编写的java类

  • 使用javah -jni 类的全限定名 生成扩展名为h的头文件

  • 使用C/C++实现本地方法

  • 将C/C++编译的文件生成动态连接库(dll文件)

  • 在java项目中引入生成的C/C++库文件

1.编写java程序

1.这里以HelloWorld为例,java代码如下:

package com.aliencat.javabase.jni;

public class HelloJni {

    public native String sayHello(String name);

    static{
        System.loadLibrary("DLL1");
    }

    public static void main(String[] args) {
        System.out.println(new HelloJni().sayHello("hello"));
    }
}

声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为 native的,并且不能实现。

Load动态库:System.loadLibrary(“hello”);加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。

2.然后进行编译

打开控制台,进入java目录下输入以下指令:

javah -jni com.aliencat.javabase.jni.HelloJni

即可生成一个一个头文件com_aliencat_javabase_jni_HelloJni.h,内容如下:

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

#ifndef _Included_com_aliencat_javabase_jni_HelloJni
#define _Included_com_aliencat_javabase_jni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
	/*
	 * Class:     com_aliencat_javabase_jni_HelloJni
	 * Method:    sayHello
	 * Signature: (Ljava/lang/String;)V
	 */
	JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
	(JNIEnv*, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

这个h文件相当于我们在java里面的接口,这里声明了一个Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv*, jobject, jstring);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。其中jobject为java的this引用,而jstring即是传入的字符串参数。

3.使用Visual Studio编写c/c++本地方法实现

下载安装Visual studi

下载地址:https://visualstudio.microsoft.com/zh-hant/vs/whatsnew/

然后安装选择以下组件即可:

在这里插入图片描述
新建项目

选择创建动态链接库

在这里插入图片描述

引入头文件

JDK目录的include目录下有一个jni.h的文件,include的win32目录下有个jni_md.h文件,还有java类生成C头文件HelloJni.h,也一起拷贝到DLL工程的目录下。主要在目录的头文件上右键选择添加->现有项:

在这里插入图片描述

用visual studio编写C代码

hello.cpp即为我创建的c++文件。

本地方法和javah命令生成的头文件里面声明的方法名相同。
代码如下:

#include "pch.h"
#include "com_aliencat_javabase_jni_HelloJni.h"
#include <iostream>
#include <stdio.h>


JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv* env, jobject, jstring jstr) {

    //将jstring类型转换成char*类型
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("GB2312");
    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); //new char[alen+1];  
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);

    printf("%s ", rtn);
    printf("this is C++ print");
    const char* str = "this is come from C++ ";
    return env->NewStringUTF(str);
}

4.生成动态库

首先选中项目根目录后右键点击属性,选择配置管理器,配置如下:

在这里插入图片描述

选中项目根目录后右键点击生成即可,控制台输出如下即表示成功:

在这里插入图片描述

5.在java项目中引入本地库

以idea为例:

在这里插入图片描述

选择你的vs编译输出的文件夹(看你的vs控制台输出的位置)即可,我的是在以下面路径下:

在这里插入图片描述

6.运行程序 java HelloJni.

加载本地库后即可使用

    static{
        System.loadLibrary("DLL1");
    }

执行main方法后输出如下:

this is come from C++ 
hello this is C++ print

JNI数据类型

Java类型本地类型(JNI)描述
boolean(布尔型)jboolean无符号8比特
byte(字节型)jbyte有符号8比特
char(字符型)jchar无符号16比特
short(短整型)jshort有符号16比特
int(整型)jint有符号32比特
long(长整型)jlong有符号64比特
float(浮点型)jfloat32比特
double(双精度浮点型)jdouble64比特
void(空型)voidN/A
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值