在许多业务场景中,字符串操作可能因复杂逻辑或高频运算成为性能瓶颈。针对这种情况,我们可以借助 C++ 的高效特性,通过 Java Native Interface (JNI),将字符串拼接功能从 Java 扩展到 C++ 实现,为性能敏感场景提供支持。本文以字符串拼接功能为例,展示如何利用 JNI 技术完成从 Java 到 C++ 的调用,包括完整的实现过程、编译步骤和运行测试。
JNI 提供了一种在 Java 和本地代码(如 C++)之间交互的标准机制。其核心流程如下:
创建一个名为StringConcatenator.java
的文件,声明本地方法,定义 Java 的 native 方法
native
关键字声明方法,仅需声明而无需实现。System.loadLibrary
或 System.load
加载本地动态链接库。package com.neo.controller;
public class StringConcatenator {
// 声明native方法
public native String concatenate(String str1, String str2);
// 加载本地动态链接库
static {
String projectDir = System.getProperty("user.dir"); // 获取当前项目目录
System.load(projectDir + "\\dll\\StringConcatenator.dll");
}
public static void main(String[] args) {
StringConcatenator concatenator = new StringConcatenator();
String result = concatenator.concatenate("Hello, ", "World!");
System.out.println("Concatenated String: " + result);
}
}
需要注意的是,如果本地库中包含多个函数,仅需调用一次 System.load
即可完成整个库的加载。加载时,只需指定库的名称,此外,在使用 javac
编译 Java 代码(例如 hello.java
)时,Java 编译器实际上并不会检查 native
方法(如 helloWorld
)的具体实现是否存在。因此,即使尚未完成本地方法的实现,也可以顺利生成 .class
字节码文件。
从 JDK 8 开始,应该使用“ javac -h
”来编译 Java 程序并生成名为JNI.h
的 C/C++ 头文件,如下所示:
javac -h . JNI.java
选项生成 C/C++ 标头并将其放置在指定的目录中(在上面的示例中, '.'
表示当前目录)。在JDK 8之前,需要使用javac
编译Java程序并使用专用的javah
实用程序生成C/C++标头,如下所示。 javah
实用程序在 JDK 10 中不再可用。
javac JNI.java
javah JNI
编译Java文件生成class
文件:(可能会遇到字符集的问题)
javac -encoding UTF-8 -h . StringConcatenator.java
生成的头文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_neo_controller_StringConcatenator */
#ifndef _Included_com_neo_controller_StringConcatenator
#define _Included_com_neo_controller_StringConcatenator
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_neo_controller_StringConcatenator
* Method: concatenate
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_neo_controller_StringConcatenator_concatenate
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
JNIEnv*
和jobject
分别是JNI环境指针和Java对象的引用。需要特别注意,native
方法对应的实现函数通常被 extern "C"
包围,这是为了让 C++ 编译器采用 C 风格的函数命名规则,而非默认的 C++ 命名规则。
extern "C"
?C++ 支持函数重载,因此在编译时会对函数名进行一种称为 名称修饰(Name Mangling) 的处理,以确保不同重载函数能够被正确区分。而 Java 中通过 JNI 调用的本地方法要求使用 C 风格的命名协议,函数名称必须与 JNI 头文件生成的声明完全一致。通过 extern "C"
,可以关闭 C++ 的名称修饰功能,确保生成的函数名与 Java 头文件中的声明匹配。
extern "C" {
JNIEXPORT void JNICALL Java_HelloWorld_printHelloWorld(JNIEnv *, jobject);
}
生成的头文件中会包含 JNI 相关的引用,例如:
#include <jni.h>
这个 jni.h
文件是 Java 提供的标准头文件,用于定义 JNI 接口,其路径位于 JDK 的安装目录下。
创建一个名为StringConcatenator.cpp
的文件,并将生成的.h文件,拷贝到月cpp文本同一层级目录下,然后实现拼接逻辑:
代码如下:
#include "com_neo_controller_StringConcatenator.h"
#include <iostream>
#include <string>
JNIEXPORT jstring JNICALL Java_com_example_jni_StringConcatenator_concatenate
(JNIEnv *env, jobject obj, jstring str1, jstring str2) {
// 获取Java字符串的C风格字符串表示
const char *cStr1 = env->GetStringUTFChars(str1, nullptr);
const char *cStr2 = env->GetStringUTFChars(str2, nullptr);
// 拼接字符串
std::string result = std::string(cStr1) + std::string(cStr2);
// 释放资源
env->ReleaseStringUTFChars(str1, cStr1);
env->ReleaseStringUTFChars(str2, cStr2);
// 返回新的Java字符串
return env->NewStringUTF(result.c_str());
}
GetStringUTFChars
获取Java字符串的C字符串表示。std::string
进行拼接。NewStringUTF
将结果返回为Java字符串。g++
)生成动态链接库。如果 JAVA_HOME
环境变量已正确配置:
g++ -shared -o StringConcatenator.dll StringConcatenator.cpp -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32"
JAVA_HOME
,请使用绝对路径:假设你的 JDK 安装在 C:\Program Files\Java\jdk1.8.0_65
,运行:
g++ -shared -o StringConcatenator.dll StringConcatenator.cpp -I"C:\Program Files\Java\jdk1.8.0_65\include" -I"C:\Program Files\Java\jdk1.8.0_65\include\win32"
在编译 C++ 的本地方法实现代码为动态链接库时,需要特别关注以下编译选项及其作用:
-fPIC
-shared
选项时必须启用此选项,否则编译期可能会报错。-shared
-o
libxxx.so
的形式,例如 libhelloWorld.so
。-I
$JAVA_HOME/include
- $JAVA_HOME/include/linux
(根据操作系统选择子目录)。调用 Java 中的 native 方法,验证 C++ 的实现功能。现在我将编译好的dll文件,放置到项目的dll文件夹中,然后同通过main方法运行程序。
native
修饰符定义方法并加载动态链接库。g++
编译生成Linux动态链接库。UnsatisfiedLinkError
错误
返回字符串乱码
内存泄漏
GetStringUTFChars
获取的字符串必须通过 ReleaseStringUTFChars
释放内存。通过本文的示例,你将掌握如何使用JNI连接Java和C++,实现简单的功能扩展。这种技术可以轻松应用于更复杂的业务场景,为项目开发带来更高效的性能支持。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。