我們把表中的每一行叫做一個“記錄”,每一個記錄包含這行中的所有資訊,就像在通訊錄資料庫中某個人全部的資訊,但記錄在資料庫中並沒有專門的記錄名,常常用它所在的行數表示這是第幾個記錄。在資料庫中存放在錶行列交叉處的資料叫做“值”,它是資料庫中最基本的儲存單元,它的位置要由這個表中的記錄和欄位來定義。
JAVA支援兩種field(欄位),每一個物件的例項都有一個物件欄位的複製;所有的物件共享一個類的靜態欄位。本地方法使用JNI提供的函式可以獲取和修改這兩種欄位。先看一個從本地程式碼中訪問物件欄位的例子:
class InstanceFieldAccess {
private String s;
private native void accessField();
public static void main(String args[]) {
InstanceFieldAccess c = new InstanceFieldAccess();
c.s = "abc";
ssField();
tln("In Java:");
tln(" c.s = "" + c.s + """);
}
static {
Library("InstanceFieldAccess");
}
}
InstanceFieldAccess這個類定義了一個物件欄位s。main方法建立了一個物件並設定s的值,然後呼叫本地方法ssField在原生代碼中列印s的值,並把它修改為一個新值。本地方法返回後,JAVA中把這個值再列印一次,可以看出來,欄位s的值已經被改變了。下面是本地方法的實現:
JNIEXPORT void JNICALL
Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj)
{
jfieldID fid; /* store the field ID */
jstring jstr;
const char *str;
/* Get a reference to obj's class */
jclass cls = (*env)->GetObjectClass(env, obj);
printf("In C:");
/* Look for the instance field s in cls */
fid = (*env)->GetFieldID(env, cls, "s",
"Ljava/lang/String;");
if (fid == NULL) {
return; /* failed to find the field */
}
/* Read the instance field s */
jstr = (*env)->GetObjectField(env, obj, fid);
str = (*env)->GetStringUTFChars(env, jstr, NULL);
if (str == NULL) {
return; /* out of memory */
}
printf(" c.s = "%s"", str);
(*env)->ReleaseStringUTFChars(env, jstr, str);
/* Create a new string and overwrite the instance field */
jstr = (*env)->NewStringUTF(env, "123");
if (jstr == NULL) {
return; /* out of memory */
}
(*env)->SetObjectField(env, obj, fid, jstr);
}
執行程式,得到輸出為:
In C:
c.s = "abc"
In Java:
c.s = "123"
4.1.1 訪問一個物件欄位的流程
為了訪問一個物件的例項欄位,本地方法需要做兩步:
首先,通過在類引用上呼叫GetFieldID獲取field ID(欄位ID)、欄位名字和欄位描述符:
Fid=(*env)->GetFieldID(env,cls,”s”,”Ljava/lang/String;”);
上例中的程式碼通過在物件引用obj上呼叫GetObjectClass獲取到類引用。一旦獲取到欄位ID,你就可以把物件和欄位ID作為引數來訪問欄位:
Jstr=(*env)->GetObjectField(env,obj,fid);
因為字串和陣列是特殊的物件,所以我們使用GetObjectField來訪問字串型別的'例項欄位。除了Get/SetObjectField,JNI還支援其它如GetIntField、SetFloatField等用來訪問基本型別欄位的函式。
4.1.2 欄位描述符
在上一節我們使用過一個特殊的C字串“Ljava/lang/String”來代表一個JVM中的欄位型別。這個字串被稱為JNI field descriptor(欄位描述符)。
字串的內容由欄位被宣告的型別決定。例如,使用“I”來表示一個int型別的欄位,“F”來表示一個float型別的欄位,“D”來表示一個double型別的欄位,“Z”來表示一個boolean型別的欄位等等。
像ng這樣的引用型別的描述符都是以L開頭,後面跟著一個JNI類描述符,以分號結尾。一個JAVA類的全名中的包名分隔符“.”被轉化成“/”。因此,對於一個欄位型別的欄位來說,它的描述符是“Ljava/lang/String”。
陣列的描述符中包含“]”字元,後面會跟著陣列型別的描述符,如“[I”是int[]型別的欄位的描述符。12.3.3詳細介紹了各種型別的欄位描述以及他們代表的JAVA型別。
你可以使用javap工具來生成欄位描述符。
4.1.3 訪問靜態欄位
訪問靜態欄位和訪問例項欄位相似,看下面這個InstanceFieldAccess例子的變形:
class StaticFielcdAccess {
private static int si;
private native void accessField();
public static void main(String args[]) {
StaticFieldAccess c = new StaticFieldAccess();
= 100;
ssField();
tln("In Java:");
tln(" = " + si);
}
static {
Library("StaticFieldAccess");
}
}
StaticFieldAccess這個類包含一個靜態欄位si,main方法建立了一個物件,初始化靜態欄位,然後呼叫本地方法ssField在原生代碼中列印靜態欄位中的值,然後設定新的值,為了演示這個值確實被改變了,在本地方法返回後,JAVA中再次這個靜態欄位的值。
下面是本地方法ssField的實現:
JNIEXPORT void JNICALL
Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj)
{
jfieldID fid; /* store the field ID */
jint si;
/* Get a reference to obj's class */
jclass cls = (*env)->GetObjectClass(env, obj);
printf("In C:");
/* Look for the static field si in cls */
fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
if (fid == NULL) {
return; /* field not found */
}
/* Access the static field si */
si = (*env)->GetStaticIntField(env, cls, fid);
printf(" = %d", si);
(*env)->SetStaticIntField(env, cls, fid, 200);
}
執行程式可得到輸出結果:
In C:
= 100
In Java:
= 200