0. 前言
Java与C++代码的交互都是通过native函数来完成,而使用函数,必然会牵涉到数据传递,数据类型决定了内存中数据的解释方式,不同的语言之间会有所不同,因此两者之间的交互,要遵守一定规则。在Java中,数据类型可以分为基本类型和引用类型,这与C++有些微不同,那么两者之间如何转换呢?Oracle官方文档给出了相关解释,本文是对官方文档的翻译和整理。
1. 基本类型
java和C++的基本数据类型,是可以直接转换的,仅在名字上有所区别。
Java类型 | Native类型 | 描述 |
---|---|---|
boolean | jboolean | 8位无符号数 |
byte | jbyte | 8位有符号数 |
char | jchar | 16位无符号数 |
short | jshort | 16位有符号数 |
int | jint | 32位有符号数 |
long | jlong | 64位有符号数 |
float | jfloat | 32位 |
double | jdouble | 64位 |
void | void | N/A |
其中,jsize和jint是同义词,jsize多用于循环计数,有如下定义
typedef jint jsize;
还有两个常量用来表示布尔值,方便使用
#define JNI_FALSE 0
#define JNI_TRUE 1
从名字上看,不难看出,从Java层面传值的基本类型,在JNI中的Native代码仅仅是在类型名前面加了一个j前缀。需要特别指出的是,C/C++代码依赖于编译器的具体编译和解释,因此在不同的编译器上,可能会有所不同。
2. 引用类型
2.1 引用类型层次结构
JNI包括许多与不同种类的Java对象对应的引用类型。JNI引用类型的层次结构如图所示。
在C语言中,所有的JNI引用类型被统一定义为jobject,如:
typedef jobject jclass;
在C++中,JNI引入了虚拟类(dummy classes)描述子类之间的关系,如下:
class _jobject {};
class _jclass : public _jobject {};
...
typedef _jobject *jobject;
typedef _jclass *jclass;
JNI中对本地引用类型的定义是通过指针来操作的,但是如果想通过JNI接口进行传值,如:在函数中传递数组或者对象等,还需要相关的JNI函数进行配合使用。
2.2 字段和方法
本地代码(C/C++)如果要调用Java中的对象方法,也就是通常所说的来自本地方法的回调(callback),需要用到JNI中的字段和方法。
JNI中对字段和方法的定义如下
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */
2.3 值类型
jvalue枚举类型通常用作参数数组中的类型元素,定义如下:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
2.4 类型签名
JNI使用Java的类型签名表示方法,详情如下表:
类型签名 | Java类型 |
---|---|
Z | Boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L 完全限定名 | 完全限定名 |
[类型 | 该类型的数组 |
(参数)类型 | 对应方法 |
方法签名主要是为了解决函数重载造成的重名问题的,通过对不同的类型进行标记,从而区别不同的重载方法。
例如,存在如下Java方法声明:
long f (int n, String s, int[] arr);
其对应的JNI类型签名为
(ILjava/lang/String;[I)J