You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI如何将Java对象数组传递到C++同类型对象数组

解决JNI中Java对象数组转C++类对象的问题

嘿,这个问题我之前做JNI开发的时候也踩过坑!咱们先搞清楚为啥报错,再一步步解决~

首先得明确:绝对不能直接把jobject强制转换成C++的Foo类型,也不能直接赋值。原因很简单:

  • Java里的Foo对象是在JVM堆上创建的,而C的Foo类是Native层的对象,两者属于完全独立的内存空间,JNI里的jobject只是JVM给Native层的一个"引用令牌",不是C对象本身,所以C++根本认不出这个类型。

那该怎么处理呢?分两种常见场景:

场景1:Java Foo和C++ Foo是数据结构对应(无Native绑定)

如果Java的Foo只是普通数据类,和C的Foo有相同的成员变量(比如都有id、name),那你需要通过JNI API读取Java对象的字段(或者调用getter方法),手动把数据复制到C的Foo对象里。

举个具体例子:
假设Java的Foo类是这样的:

public class Foo {
    private int id;
    private String name;

    public Foo(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() { return id; }
    public String getName() { return name; }
}

对应的C++ Foo类:

#include <cstring>
struct Foo {
    int id;
    char name[256]; // 假设用固定长度数组存字符串
};

那你的JNI代码应该改成这样:

#include "headers/Foo.h"
#include <vector>
#include <cstring>

extern "C" JNIEXPORT void JNICALL Java_main_Main_Interface(JNIEnv *env, jobject obj, jobjectArray fooArray){
    jint size = env->GetArrayLength(fooArray);
    if (size == 0) return;

    // 1. 获取Java Foo类的Class引用和方法ID
    jobject firstFoo = env->GetObjectArrayElement(fooArray, 0);
    jclass fooCls = env->GetObjectClass(firstFoo);
    // 获取getId()方法的ID:签名()I表示无参返回int
    jmethodID getIdMethod = env->GetMethodID(fooCls, "getId", "()I");
    // 获取getName()方法的ID:签名()Ljava/lang/String;表示无参返回String
    jmethodID getNameMethod = env->GetMethodID(fooCls, "getName", "()Ljava/lang/String;");

    // 2. 遍历数组,逐个转换为C++ Foo对象
    // 这里用std::vector代替栈上数组,避免C++标准不支持变长栈数组的问题
    std::vector<Foo> cfooList;
    cfooList.reserve(size);

    for(int i = 0; i < size; i++){
        jobject javaFoo = env->GetObjectArrayElement(fooArray, i);
        // 调用getter获取Java对象的成员值
        jint id = env->CallIntMethod(javaFoo, getIdMethod);
        jstring nameStr = static_cast<jstring>(env->CallObjectMethod(javaFoo, getNameMethod));
        const char* nameUtf = env->GetStringUTFChars(nameStr, nullptr);

        // 构造C++ Foo对象
        Foo cfoo;
        cfoo.id = id;
        // 复制字符串,注意截断避免越界
        std::strncpy(cfoo.name, nameUtf, sizeof(cfoo.name) - 1);
        cfoo.name[sizeof(cfoo.name) - 1] = '\0'; // 确保字符串以'\0'结尾

        cfooList.push_back(cfoo);

        // 3. 释放JNI资源,避免内存泄漏和局部引用溢出
        env->ReleaseStringUTFChars(nameStr, nameUtf);
        env->DeleteLocalRef(javaFoo);
    }

    // 释放剩余的局部引用
    env->DeleteLocalRef(firstFoo);
    env->DeleteLocalRef(fooCls);

    // 现在你可以使用cfooList里的C++ Foo对象了
}

这里要注意几个细节:

  • 不要用Foo cfoo[size]这种栈上变长数组,C++标准不支持,建议用std::vector或者new分配堆内存。
  • 用完JNI的局部引用(比如javaFoo、firstFoo)一定要调用DeleteLocalRef,不然会占满JVM的局部引用表导致崩溃。
  • 字符串处理完必须调用ReleaseStringUTFChars,释放JVM分配的字符串内存。

场景2:Java Foo是绑定Native实现的类

如果你的Java Foo类本身就对应一个Native层的Foo对象(比如Java类里有native方法,需要调用C++的Foo逻辑),那可以让Java类持有一个Native对象的指针,在Native层取出这个指针直接使用。

步骤如下:

  1. 修改Java Foo类,添加一个存储Native指针的字段:
public class Foo {
    // 存储C++ Foo对象的指针,用long类型(因为指针在64位系统是8字节,long刚好能存)
    private long nativePtr;

    public Foo() {
        // 调用native方法创建C++ Foo对象,并把指针存下来
        nativePtr = createNativeFoo();
    }

    // 创建Native Foo的native方法
    private native long createNativeFoo();
    // 销毁Native Foo的native方法,避免内存泄漏
    public native void destroyNativeFoo();

    // 重写finalize,确保对象被回收时销毁Native资源
    @Override
    protected void finalize() throws Throwable {
        destroyNativeFoo();
        super.finalize();
    }
}
  1. 编写对应的Native代码:
#include "headers/Foo.h"

// 创建C++ Foo对象,返回指针
extern "C" JNIEXPORT jlong JNICALL Java_com_example_Foo_createNativeFoo(JNIEnv* env, jobject obj) {
    Foo* foo = new Foo();
    // 把指针转换成jlong返回给Java
    return reinterpret_cast<jlong>(foo);
}

// 销毁C++ Foo对象
extern "C" JNIEXPORT void JNICALL Java_com_example_Foo_destroyNativeFoo(JNIEnv* env, jobject obj) {
    jclass cls = env->GetObjectClass(obj);
    // 获取nativePtr字段的ID,签名J表示long类型
    jfieldID ptrField = env->GetFieldID(cls, "nativePtr", "J");
    jlong ptrVal = env->GetLongField(obj, ptrField);
    if (ptrVal != 0) {
        Foo* foo = reinterpret_cast<Foo*>(ptrVal);
        delete foo;
        // 把指针置0,避免重复销毁
        env->SetLongField(obj, ptrField, 0);
    }
    env->DeleteLocalRef(cls);
}

// 处理Java Foo数组的JNI方法
extern "C" JNIEXPORT void JNICALL Java_main_Main_Interface(JNIEnv *env, jobject obj, jobjectArray fooArray){
    jint size = env->GetArrayLength(fooArray);
    if (size == 0) return;

    // 获取Java Foo类的Class引用和nativePtr字段ID
    jclass fooCls = env->FindClass("com/example/Foo");
    jfieldID ptrField = env->GetFieldID(fooCls, "nativePtr", "J");

    std::vector<Foo*> cfooPtrs;
    cfooPtrs.reserve(size);

    for(int i = 0; i < size; i++){
        jobject javaFoo = env->GetObjectArrayElement(fooArray, i);
        // 取出nativePtr的值,转成C++ Foo指针
        jlong ptrVal = env->GetLongField(javaFoo, ptrField);
        Foo* cfoo = reinterpret_cast<Foo*>(ptrVal);
        cfooPtrs.push_back(cfoo);

        env->DeleteLocalRef(javaFoo);
    }

    // 现在你可以直接使用cfooPtrs里的C++ Foo指针操作Native对象了

    env->DeleteLocalRef(fooCls);
}

这种方式的好处是不需要复制数据,直接操作Native层的对象,效率更高,但需要维护Native对象的生命周期,避免内存泄漏。


内容的提问来源于stack exchange,提问作者bcsta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:25:34