发布于2026-07-08 阅读(0)
扫一扫,手机访问
最近在做一个项目,需要从 C/C++ 环境直接调用 Ja va 类里的特定方法——注意,可不是用 system() 去启动一个 Ja va 进程,而是通过 JNI(Ja va Native Interface)在同一个进程空间里动态加载 JVM,找到对应的类,再调用其方法。听起来有点绕?把整个流程拆解开来,其实几步就能说清楚。
先给一个总体框架:
下面按照实战步骤,把每个环节拆开细讲。
我的实验环境是 RedHat Enterprise Linux 5,内核 2.6.18。安装 JDK 没什么特别的,去官网或者软件库下载对应 Linux 平台的安装包即可。我下载的是 jdk-1_5_0_06-linux-i586.bin,解压安装:
# mkdir /usr/local/ja va
# cd /usr/local/ja va
# cp /home/soft/jdk-1_5_0_06-linux-i586.bin ./
# chmod u+x jdk-1_5_0_06-linux-i586.bin
# ./jdk-1_5_0_06-linux-i586.bin
安装完成后,JDK 在 /usr/local/ja va/jdk1.5.0_06/。验证一下:
# cd jdk1.5.0_06/bin
# ./ja va -version
ja va version "1.5.0_06"
Ja va(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Ja va HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)
然后是环境变量配置。这一步非常重要,尤其是 JRE_HOME 和 LD_LIBRARY_PATH,后面编译 C++ 程序时要用到 JRE 下的 libjvm.so。我的设置如下:
export JA VA_HOME=/usr/local/ja va/jdk1.5.0_06
export PATH=$PATH:/usr/local/ja va/jre1.5.0_05/bin
export JRE_HOME=/usr/lib/jvm/ja va-1.4.2-gcj-1.4.2.0/jre
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JRE_HOME/lib/i386:$JRE_HOME/lib/i386/client
注意,如果机器上没有 JRE 环境,需要先安装 JRE(安装方法与 JDK 类似),否则后续 libjvm.so 找不到,编译会报错。
设置完后可以检查变量是否生效:
[root@localhost bin]# echo $JA VA_HOME
/usr/local/ja va/jdk1.5.0_06
定义一个简单的 MyTest 类,放在包 com.test 下。包含一个静态方法 add 和一个实例方法 judge:
package com.test;
public class MyTest {
public MyTest(){
super();
}
public static int add(int a,int b) {
return a+b;
}
public boolean judge(boolean bool) {
return !bool;
}
}
编译:
# ja vac MyTest.ja va
编译后生成 MyTest.class,把它放到当前目录的 com/test 子目录下。C++ 程序在 JNI 查找时会按照全限定类名 com/test/MyTest 去寻找这个 class 文件。
接下来是核心——C++ 端代码。包含必要的头文件,并实现两个辅助函数 stoJstring 和 jstringTostring 用于 Ja va 字符串与 C 字符串之间的转换(本示例中虽然没直接用字符串,但保留作为通用工具)。main 函数内的流程就是标准的 JNI 调用步骤:
JNI_CreateJa vaVM 创建 Ja va 虚拟机。FindClass 找到目标类。GetStaticMethodID,实例方法用 GetMethodID。CallStaticIntMethod,实例方法需要先创建对象再调用)。完整代码如下:
#include
#include
#include
#include
#include
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("ja va/lang/String");
jmethodID ctorID = env->GetMethodID(strClass, "", "([BLja va/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}
char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("ja va/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Lja va/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);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}
using namespace std;
int main()
{
Ja vaVMOption options[2];
JNIEnv *env;
Ja vaVM *jvm;
Ja vaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jint square;
jboolean jnot;
jobject jobj;
options[0].optionString = "-Dja va.compiler=NONE";
options[1].optionString = "-Dja va.class.path=.";
// options[2].optionString = "-verbose:jni"; // 调试时可开启
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
status = JNI_CreateJa vaVM(&jvm, (void**)&env, &vm_args);
if(status != JNI_ERR){
printf("create ja va jvm success\n");
cls = env->FindClass("com/test/MyTest");
if(cls !=0){
printf("find ja va class success\n");
// 构造函数
mid = env->GetMethodID(cls,"","()V");
if(mid !=0){
jobj=env->NewObject(cls,mid);
std::cout << "init ok" << std::endl;
}
// 调用add静态函数
mid = env->GetStaticMethodID( cls, "add", "(II)I");
if(mid !=0){
square = env->CallStaticIntMethod( cls, mid, 5,5);
std::cout << square << std::endl;
}
// 调用judge实例函数
mid = env->GetMethodID( cls, "judge","(Z)Z");
if(mid !=0){
jnot = env->CallBooleanMethod(jobj, mid, 1);
if(!jnot) std::cout << "Boolean ok" << std::endl;
}
}
else{
fprintf(stderr, "FindClass failed\n");
}
jvm->DestroyJa vaVM();
fprintf(stdout, "Ja va VM destory.\n");
return 0;
}
else{
printf("create ja va jvm fail\n");
return -1;
}
}
编译 C++ 程序时,需要链接 libjvm.so。注意编译命令中 -I 指定 JNI 头文件路径,-L 指定库文件路径。我这里使用的是 ${JRE_HOME}/lib/i386/client 下的 libjvm.so,而不是 ${JA VA_HOME} 下的。一开始我图省事用了 JDK 目录下的版本,结果运行时出现了令人崩溃的 SIGSEGV 错误:
# An unexpected error has been detected by HotSpot Virtual Machine:
# SIGSEGV (0xb) at pc=0xb6d3dbe3, pid=14454, tid=2773482416
# Ja va VM: Ja va HotSpot(TM) Server VM (1.5.0_11-b03 mixed mode)
...
后来换成 JRE 下的 libjvm.so 就一切正常了。编译命令示例:
g++ -o testja va testja va.cpp -I${JA VA_HOME}/include -I${JA VA_HOME}/include/linux -L${JRE_HOME}/lib/i386/client -ljvm
编译完成后可以用 ldd testja va 检查链接库是否正确。
[root@localhost jni]# ./testja va
create ja va jvm success
find ja va class success
init ok
10
Boolean ok
Ja va VM destory.
输出的 10 就是 add(5,5) 的结果,Boolean ok 说明 judge(true) 返回了 false(取反正确)。整个流程跑通,C++ 调用 Ja va 就完成了。

售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8