JNI之三:JNI类型和数据结构

本文将讨论JNI是如何将Java类型映射到native C类型的。

基本类型

下表展示了Java基本类型以及与它们对等的native类型:

Java类型native类型描述
booleanjboolean无符号(unsigned)8bits
bytejbyte有符号(signed)8bits
charjcharunsigned 16bits
shortjshortsigned 16bits
intjintsigned 32bits
longjlongsigned 64bits
floatjfloat32bits
doublejdouble64bits
voidvoid-

以下定义是为了提供方便:

#define JNI_FALSE  0
#define JNI_TRUE   1

jsize整数类型用来描述基数指数和大小:typedef jint jsize;

引用类型

JNI包含许多不同种类的Java对象对应不同的引用类型,JNI引用类型的层级结构如下:

  • jobject
    • jclass(java.lang.Class objects)
    • jstring(java.lang.String objects)
    • jarray(arrays)
      • jobjectArray(object arrays)
      • jbooleanArray(boolean arrays)
      • jbyteArray (byte arrays)
      • jcharArray (char arrays)
      • jshortArray (short arrays)
      • jintArray (int arrays)
      • jlongArray (long arrays)
      • jfloatArray (float arrays)
      • jdoubleArray (double arrays)
    • jthrowable (java.lang.Throwable objects)

在C中,其它所有的JNI引用都被定义为jobject,如:

typedef jobject jclass;

在C++中,JNI引入了一组伪类来加强子类型关系。例如:

class _jobject {};
class _jclass : public _jobject {};
// ...
typedef _jobject *jobject;
typedef _jclass *jclass;

字段和方法id

字段和方法id是常规的C指针类型:

struct _jfieldID;              /* opaque structure */
typedef struct _jfieldID *jfieldID;   /* field IDs */

struct _jmethodID;              /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */

值类型

联合体jvalue用作参数数组的元素类型,定义如下:

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

类型签名

JNI使用JVM的类型签名表示法,下表展示了类型签名。

Java类型native类型描述
booleanjboolean无符号(unsigned)8bits
bytejbyte有符号(signed)8bits
charjcharunsigned 16bits
shortjshortsigned 16bits
intjintsigned 32bits
longjlongsigned 64bits
floatjfloat32bits
doublejdouble64bits
voidvoid-

例如Java方法

long f (int n, String s, int[] arr);

它的类型签名如下

(ILjava/lang/String;[I)J

Modified UTF-8(MUTF-8)字符串

JNI使用modified UTF-8字符串表示各种字符串类型。Modified UTF-8字符串也是JVM中使用的。Modified UTF-8 经过编码,每个字符仅使用一个字节就能表示仅包含非空ASCII字符的字符序列,但所有的Unicode字符都能被表示。

\u0001\u007区间内的所有的字符被单一字节表示,如

  • 0xxxxxxx

这个字节中的7位数据是字符表示的值

空字符(’\u0000‘)和 从'\u0080' 到 '\u07FF' 区间中的字符用一对字节x和y表示:

  • x: 110xxxxx
  • y: 10yyyyyy

这些字节代表值为 ((x & 0x1f) << 6) + (y & 0x3f)的字符串。

'\u0800' 到'\uFFFF'范围的字符由3个字节x,y和z表示:

  • x: 1110xxxx
  • y: 10yyyyyy
  • z: 10zzzzzz

这些字节代表值为 ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f) 的字符串。

码位(code point)在U+FFFF之上(所谓的补充字符)的字符分别由两个UTF-16表示的替代码单元表示。每个替代码单元由3个字节表示,这就意味着补充字符由6个字节表示,uvwxy, 及 z:

  • u: 11101101
  • v: 1010vvvv
  • w: 10wwwwww
  • x: 11101101
  • y: 1011yyyy
  • z: 10zzzzzz

值为0x10000+((v&0x0f)<<16)+((w&0x3f)<<10)+(y&0x0f)<<6)+(z&0x3f)的字符由6字节表示。

多字节字符的字节在class中以大端序(big-endian)的方式存储。

MUTF-8和标准的UTF-8由两个区别。首先,空字符(char)0使用二字节格式编码,而不是一字节。这就意味着MUTF-8字符串不嵌入空字符。其次,只有一字节、二字节和三字节格式的标准UTF-8被使用。JVM不承认四字节的标准UTF-8字符,它使用自己的两倍三字节(two-times-three-byte)格式代替。

获取更多关于标准UTF-8的信息,查阅 Ecoding Forms of The Unicode Standard, Version 4.0