Java对象在JNI中无法按值传递,因其仅以jobject引用形式传递,native代码无法直接复制JVM管理的对象结构;实际需通过显式提取字段、转换类型并构造C++副本实现语义上的“按值使用”。

Java 通过 JNI 调用 C++ 时,无法真正按值传递 Java 对象。JNI 规范中,所有 Java 对象(包括数组、String、自定义类实例)在 native 方法参数中都以 jobject(或其子类型如 jstring、jclass 等)形式传递——这本质上是对 JVM 堆中对象的引用(handle),不是对象内容的拷贝。
为什么 Java 对象不能“按值传递”?
JVM 管理对象生命周期和内存布局,native 代码无权直接访问或复制 Java 对象的内部结构(如字段偏移、vtable、GC 元信息等)。即使你尝试用 GetObjectRefType 或反射获取字段值,也只是读取/写入字段内容,而非复制整个对象实例。
- JNI 层面只有
jobject引用,没有“对象值”的二进制表示标准 - Java 对象可能被 GC 移动,native 侧无法安全持有原始内存地址
- 对象可能包含 final 字段、不可变状态、锁、引用循环等,无法简单深拷贝
实际可行的替代方案
若目标是让 C++ 侧获得一份独立、可自由操作的数据副本(即“语义上按值使用”),需显式转换和复制:
-
基本类型包装类:如
Integer、Double,用GetIntValue、GetDoubleValue提取原始值,C++ 中用int、double变量接收——这是真正的按值 -
String:调用
GetStringUTFChars或GetStringCritical获取 C 风格字符串指针,再用std::string构造副本;用完及时ReleaseStringUTFChars -
数组:用
GetIntArrayRegion、GetObjectArrayElement等批量读取元素,存入 C++std::vector或原生数组 -
自定义 Java 对象:在 Java 侧提供
toNativeStruct()方法(返回byte[]或多个基本类型字段),或在 native 侧用GetObjectClass+GetMethodID+CallXXXMethod逐个读取字段值,组装为 C++ struct
注意内存与生命周期管理
所有从 JNI 接口获取的本地副本(如 const char*、jint*)都是临时的,必须在当前 native 方法返回前完成使用并释放(如 ReleaseStringUTFChars、ReleaseIntArrayElements)。不要缓存这些指针到后续调用中。
立即学习“Java免费学习笔记(深入)”;
- 全局引用(
NewGlobalRef)可延长对象生命周期,但仍是引用,不是值 - 局部引用(默认)在 native 方法返回后自动失效
- C++ 侧构造的 struct / vector 是纯本地内存,完全独立于 JVM
小结
JNI 没有“Java 对象按值传递”机制。所谓“按值使用”,本质是在 native 侧主动提取、转换、复制 Java 对象的数据内容,生成一份脱离 JVM 管理的 C++ 副本。关键在于明确数据边界、手动做序列化/反序列化,并严格管理 JNI 引用和本地内存。


















