如何在C++程序中调用返回Java类对象的方法并处理该对象
JNI中Java对象与C++对象的互转问题解答
问题1:将JNI获取的jobject转换为C++的Test1对象
首先,你得在C++里定义一个和Java端Test1结构完全匹配的类,这样才能存储对应的数据:
// C++端的Test1类,和Java字段一一对应 class Test1 { public: int a; int b; };
接下来,你需要通过JNI的API读取Java对象里的字段值,再赋值给C++对象。具体步骤如下:
- 找到Java端
Test1类的jclass引用 - 获取
a和b两个字段的jfieldID(JNI用来定位字段的唯一标识) - 从拿到的
jobject中取出字段的int值,转成C++的int类型后赋值
把这段代码加到你获取jobject obj之后的位置:
// 找到Test1类,如果有包名要写完整路径,比如"com/yourpackage/Test1" jclass test1Cls = env->FindClass("Test1"); if (test1Cls != nullptr) { // 获取字段ID,第二个参数是字段名,第三个是字段签名:"I"表示int类型 jfieldID fieldA = env->GetFieldID(test1Cls, "a", "I"); jfieldID fieldB = env->GetFieldID(test1Cls, "b", "I"); if (fieldA != nullptr && fieldB != nullptr) { // 读取Java对象中的字段值 jint javaA = env->GetIntField(obj, fieldA); jint javaB = env->GetIntField(obj, fieldB); // 赋值给C++的Test1对象 Test1 cppTest1; cppTest1.a = static_cast<int>(javaA); cppTest1.b = static_cast<int>(javaB); // 现在就可以正常使用cppTest1了 std::cout << "C++ Test1对象: a=" << cppTest1.a << ", b=" << cppTest1.b << std::endl; } else { std::cerr << "无法获取Test1的字段ID" << std::endl; } } else { std::cerr << "找不到Test1类" << std::endl; }
注意:如果Java的Test1不在默认包下,FindClass的参数必须是完整的类路径,用斜杠代替点,比如"com/example/Test1"
补充问题:将C++的Test1对象传递给Java方法
要实现这个,需要分两步:在Java端准备接收方法,然后在C++端创建Java对象并传递,具体如下:
1. Java端准备接收方法
先在Server11类里定义一个静态方法,用来接收Test1对象:
public class Server11 { // 其他已有方法... public static void acceptTest1(Test1 testObj) { System.out.println("Java收到Test1对象: a=" + testObj.a + ", b=" + testObj.b); } }
2. C++端创建Java的Test1对象并传递
你需要先创建一个Java的Test1实例,把C++对象的字段值赋值给它,再调用Java的接收方法:
// 先定义一个C++的Test1对象,准备传递 Test1 cppTest1; cppTest1.a = 100; cppTest1.b = 200; // 找到Java的Test1类 jclass test1Cls = env->FindClass("Test1"); if (test1Cls != nullptr) { // 获取Test1的无参构造方法ID,"<init>"是构造方法的固定名称,"()V"是无参构造的签名 jmethodID test1Ctor = env->GetMethodID(test1Cls, "<init>", "()V"); if (test1Ctor != nullptr) { // 创建Java的Test1对象实例 jobject javaTest1 = env->NewObject(test1Cls, test1Ctor); // 获取字段ID,给Java对象赋值 jfieldID fieldA = env->GetFieldID(test1Cls, "a", "I"); jfieldID fieldB = env->GetFieldID(test1Cls, "b", "I"); if (fieldA != nullptr && fieldB != nullptr) { env->SetIntField(javaTest1, fieldA, cppTest1.a); env->SetIntField(javaTest1, fieldB, cppTest1.b); // 找到Server11类,调用acceptTest1方法 jclass serverCls = env->FindClass("Server11"); if (serverCls != nullptr) { // 方法签名:(LTest1;)V,其中LTest1;表示参数是Test1对象,V表示返回值是void jmethodID acceptMethod = env->GetStaticMethodID(serverCls, "acceptTest1", "(LTest1;)V"); if (acceptMethod != nullptr) { // 调用静态方法,传递JavaTest1对象 env->CallStaticVoidMethod(serverCls, acceptMethod, javaTest1); } else { std::cerr << "找不到acceptTest1方法ID" << std::endl; } } else { std::cerr << "找不到Server11类" << std::endl; } } else { std::cerr << "无法获取Test1的字段ID" << std::endl; } } else { std::cerr << "找不到Test1的构造方法ID" << std::endl; } } else { std::cerr << "找不到Test1类" << std::endl; }
关于方法签名的说明
如果Java的Test1在包下,比如com.example.Test1,那么方法签名里的参数部分要写成(Lcom/example/Test1;)V,完整的签名规则是:
(表示参数开始L表示引用类型,后面跟完整类路径(斜杠分隔),最后加;)表示参数结束V表示返回值是void
通用注意事项
- 所有JNI返回的
jclass、jmethodID、jfieldID都必须做非空检查,否则一旦为空就会导致JNI调用崩溃 - 局部引用(比如
FindClass返回的jclass)不需要手动释放,JNI会在当前线程的方法执行完毕后自动回收;如果是全局引用(用NewGlobalRef创建的),则需要手动调用DeleteGlobalRef释放 - 类路径和签名一定要准确,JNI对这些字符串的格式要求非常严格,写错会直接找不到类或方法/字段
内容的提问来源于stack exchange,提问作者Himanshu Sharma
相关产品推荐
相关产品推荐

