本篇讲述如何在本地方法中操作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中极其类似吧!