本篇讲述如何在本地方法中操作java对象的属性。该属性可以是实例变量也可以是类变量。下面是两个例子。
一、 操作属性
类的属性包含两种:实例变量和类变量。下面就分别讲解吧!(很多读者都说以前的关于JNI文章讲解太少了,不太能看懂,下面的文章我将比较详细地讲解。)
1.1实例变量
下面是一个包含一个本地方法的类,该类有基本数据类型型的属性、String类型的属性以及自定义类型的属性,几乎可以包含我们所有类型的实例属性。
// InstanceFieldAccess.java
public class InstanceFieldAccess {
//下面是3种类型的实例变量
private String str;
private int attr ;
private Employee emp ;
private native void accessField(); //访问以上属性的本地方法
public static void main(String args[]) { //main方法,不用说了吧!
InstanceFieldAccess c = new InstanceFieldAccess(); //实例化该对象
c.str = "abc"; //不是private的,怎么可以使用啊?想想吧!为什么
c.attr = 5; //private是内外不能访问,这可是在类的内部哦!
c.emp=new Employee("master24",28); //创建一个引用类型的属性
c.accessField(); //调用该本地方法,实现在后面
System.out.println("In Java:"); //在java中输出
System.out.println(" c.str = "" + c.str + " ""); //看看在JNI中操作的属性,
//在java中是否有“后遗症”
}
static { //在类构造前,加载动态链接库
System.loadLibrary("InstanceFieldAccess");
}
}
class Employee{ //一个辅助类,我们会用它做一个复杂的调用
private String ;
private int age =-1;
Employee(){ }
Employee(String n , int a ){
name = n ;
age =a ;
}
public void setName(String n){ name = n ; }
public void setAge(int a){ age =a ; }
public String getName(){ return name ; }
public int getAge(){ return age ; }
}
//InstanceFieldAccess.c 在你的VC中建立的C文件,不会请参考第一篇
#include <stdio.h>
#include <stdarg.h>
#include <jni.h>
JNIEXPORT void JNICALL Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj){
//以下三段,不同色的分别演示操作String类型的属性、int类型的属性、对象属性
jfieldID fid; //存放属性(field)的标识符(ID),看到了吧,一般称fid
jstring jstr; //JNI中的String
const char *str; //
jint int1 ; //JNI中的int
jobject empobj ; //属性Employee的实例
jclass empcls; //属性Employee的类
jclass cls = (*env)->GetObjectClass(env, obj); //得到该对象的class,第一步哦!
printf("In C: n"); //本地输出方法
fid = (*env)->GetFieldID(env, cls, "str","Ljava/lang/String;"); //得到属性的标识符
//第一个参数为env
//第二个参数为上面第一步的class
//第三个是InstanceFieldAccess.java
// 中属性的名称
//第四个是方法的返回值,将Signature
// 第二步哦!
//实例变量和类的变量使用不同方法
if (fid == NULL) {
return; //没有得到此fid就返回
}
jstr = (*env)->GetObjectField(env, obj, fid); //得到实例的属性,第三步哦!
//一般格式为Get <Type>Field
//实例变量和类变量不同
str = (*env)->GetStringUTFChars(env, jstr, NULL); //操作String的方法
if (str == NULL) { return; }
printf(" c.str = "%s " n", str); //输出此String
(*env)->ReleaseStringUTFChars(env, jstr, str); //释放此String
jstr = (*env)->NewStringUTF(env, "mater24"); //创建一个String
if (jstr == NULL) { return; }
(*env)->SetObjectField(env, obj, fid, jstr); //将此String赋值给此成员变量
//第二部分
fid = (*env)->GetFieldID(env, cls, "attr","I"); //得到类型为int属性attr的ID
if (fid == NULL) { return; }
int1 = (*env)->GetIntField(env, obj, fid); //看到了吧!Get<Type>Field
if (int1 == NULL) { return; }
printf(" c.attr = "%d " n", int1); //输出此int
printf(" c.attr = "%d " n", int1);
//第三部分
fid = (*env)->GetFieldID(env, cls, "emp","LEmployee;");//得到类型为Employee属性emp的ID
if (fid == NULL) { return; }
empobj = (*env)->GetObjectField(env, obj, fid); //同上,Get<Type>Field
if (empobj== NULL) { return; } //此时我们得到一个Employee的实例
empcls = (*env)->GetObjectClass(env, empobj); //得到Employee的Class
printf("In C: n Use Employee Instance !"); //在本地代码中输出
fid = (*env)->GetFieldID(env, empcls , "name","Ljava/lang/String;");
//继续得到Employee的属性的ID
if (fid == NULL) { return; } //属性类型为String,名称为name
jstr = (*env)->GetObjectField(env, empobj, fid); //得到那么属性的值
str = (*env)->GetStringUTFChars(env, jstr, NULL); //得到此String的拷贝
if (str == NULL) { return; }
printf(" e.name = "%s " n", str);
(*env)->ReleaseStringUTFChars(env, jstr, str); //释放,不过肯定少释放一次!找找在哪里
}
操作类的实例属性分为三步:
1. 得到类的类。使用函数:
jclass GetObjectClass(JNIEnv *env, jobject obj);
2. 得到你要要的实例属性的标识符。使用函数:
jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
3. 得到该属性对应的值。使用函数
<NativeType> Get<Type>Field(JNIEnv *env,jobject obj, jfieldID fieldID);
一定要看好了参数哦!
1.2 静态成员变量
这个例子相对比较简单点!一个包含本地方法的类!主要包含两个静态的属性,一个为String类型,另外一个为自定义Manager类型!
public class StaticFieldAccess {
private static String str;
private static Manager mgr = new Manager() ;
private native void accessStaticField(); //本地方法
public static void main(String args[]) {
StaticFieldAccess sfa = new StaticFieldAccess(); //创建类的实例
StaticFieldAccess.str = "I'm Static!"; //给类的静态属性赋值
sfa.accessStaticField(); //调用本地方法
System.out.println("In Java:"); //java中输出
System.out.println(" StaticFieldAccess.str = " + str);
}
static {
System.loadLibrary("StaticFieldAccess");
}
}
class Manager{ //简单的包含静态属性的类
static String company="Microsoft!";
}
JNI的实现。主要演示:1.访问类的String类型的静态属性str;2.访问类的Manager类型的属性mgr,接着访问该实例的String类型的静态属性company。看起来较复杂,好好理解就很很简单了!
#include <jni.h>
JNIEXPORT void JNICALL Java_StaticFieldAccess_accessStaticField (JNIEnv *env , jobject obj){
//一系列访问类的静态属性用到的中间变量
jclass cls ; //该类的类
jfieldID fid ; //方法的标识符
jobject str ; //String类型属性的对象
const char *jstr ; //JNI中的String
jstring newstr ; //JNI中新创建的String
//相应地以下是访问类的Manager类型静态属性(此时得到Manager-mgr)
//的静态的String类型的属性的中间变量(此时得到company)
jclass mgrcls ;
jobject mgrobj ;
jfieldID mgrfid ;
jfieldID cpyfid ;
jobject mgrstr ;
const char *cpystr;
cls = (*env)->GetObjectClass(env, obj);
printf("In C: n");
fid = (*env)->GetStaticFieldID(env, cls, "str", "Ljava/lang/String;");
if (fid == NULL) { return; }
str= (*env)->GetStaticObjectField(env, cls, fid);
jstr = (*env)->GetStringUTFChars(env, str, NULL);
if (jstr == NULL) { return; }
printf(" StaticFieldAccess.str = %s n", jstr);
(*env)->ReleaseStringUTFChars(env, str, jstr); //释放此String
newstr = (*env)->NewStringUTF(env, "XXXXXXXXXXXXX");
if (newstr == NULL) { return; }
(*env)->SetStaticObjectField(env, cls, fid, newstr);
printf("In C: n Use Manager Class !");
mgrfid = (*env)->GetStaticFieldID(env, cls, "mgr", "LManager;");
if (mgrfid == NULL) { return; }
mgrobj = (*env)->GetStaticObjectField(env, cls, mgrfid); //cls
mgrcls= (*env)->GetObjectClass(env, mgrobj);
cpyfid = (*env)->GetStaticFieldID(env, mgrcls, "company", "Ljava/lang/String;");
if (cpyfid == NULL) { return; }
mgrstr = (*env)->GetStaticObjectField(env, mgrcls, cpyfid);'
cpystr = (*env)->GetStringUTFChars(env, mgrstr, NULL); //得到此String的拷贝
if (cpystr == NULL) { return; }
printf(" StaticFieldAccess.mgr.Company = %s n", cpystr);
(*env)->ReleaseStringUTFChars(env, mgrstr, cpystr); //释放此String
}
以下是执行结果!
In Java:
StaticFieldAccess.str = XXXXXXXXXXXXX
In C:
StaticFieldAccess.str = I'm Static!
In C:
Use Manager Class ! StaticFieldAccess.mgr.Company = Microsoft!
操作类的实例属性分为三步:
1. 得到类的类。使用函数:
jclass GetObjectClass(JNIEnv *env, jobject obj);
2. 得到你要要的实例属性的标识符。使用函数:
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
3. 得到该属性对应的值。使用函数
<NativeType> GetStatic<Type>Field(JNIEnv *env,jclass cls , jfieldID fieldID);
一定要看好了参数哦!
以前都使用那个黑漆漆的贴图,现在使用文字结果了!怎么搞啊!
java StaticFieldAccess >1.txt 这样你运行StaticFieldAccess的结果就放到与你的类同目录的文本文件1.txt中了!有意思吧!
1.3操作属性的差异:
主要在第2、3步。第二步GetFieldID 和GetStaticFieldID看到了吧!参数的个数和意义相同!第三步Get<Type>Field和GetStatic<Type>Field名称不同,且第二个参数,实例属性的为jobject,类属性的为jclass。和java中极其类似吧!