JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
来源:互联网 发布:freemind软件使用方法 编辑:程序博客网 时间:2024/06/06 16:35
转载请注明出处:http://blog.csdn.net/xyang81/article/details/42836783
在上一章中我们学习到了如何在本地代码中访问任意Java类中的静态方法和实例方法,本章我们也通过一个示例来学习Java中的实例变量和静态变量,在本地代码中如何来访问和修改。静态变量也称为类变量(属性),在所有实例对象中共享同一份数据,可以直接通过【类名.变量名】来访问。实例变量也称为成员变量(属性),每个实例都拥有一份实例变量数据的拷贝,它们之间修改后的数据互不影响。下面看一个例子:
- package com.study.jnilearn;
- /**
- * C/C++访问类的实例变量和静态变量
- * @author yangxin
- */
- public class AccessField {
- private native static void accessInstanceField(ClassField obj);
- private native static void accessStaticField();
- public static void main(String[] args) {
- ClassField obj = new ClassField();
- obj.setNum(10);
- obj.setStr("Hello");
- // 本地代码访问和修改ClassField为中的静态属性num
- accessStaticField();
- accessInstanceField(obj);
- // 输出本地代码修改过后的值
- System.out.println("In Java--->ClassField.num = " + obj.getNum());
- System.out.println("In Java--->ClassField.str = " + obj.getStr());
- }
- static {
- System.loadLibrary("AccessField");
- }
- }
AccessField是程序的入口类,定义了两个native方法:accessInstanceField和accessStaticField,分别用于演示在本地代码中访问Java类中的实例变量和静态变量。其中accessInstaceField方法访问的是类的实例变量,所以该方法需要一个ClassField实例作为形参,用于访问该对象中的实例变量。
- package com.study.jnilearn;
- /**
- * ClassField.java
- * 用于本地代码访问和修改该类的属性
- * @author yangxin
- *
- */
- public class ClassField {
- private static int num;
- private String str;
- public int getNum() {
- return num;
- }
- public void setNum(int num) {
- ClassField.num = num;
- }
- public String getStr() {
- return str;
- }
- public void setStr(String str) {
- this.str = str;
- }
- }
在本例中没有将实例变量和静态变量定义在程序入口类中,新建了一个ClassField的类来定义类的属性,目的是为了加深在C/C++代码中可以访问任意Java类中的属性。在这个类中定义了一个int类型的实例变量num,和一个java.lang.String类型的静态变量str。这两个变量会被本地代码访问和修改。
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_study_jnilearn_AccessField */
- #ifndef _Included_com_study_jnilearn_AccessField
- #define _Included_com_study_jnilearn_AccessField
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_study_jnilearn_AccessField
- * Method: accessInstanceField
- * Signature: (Lcom/study/jnilearn/ClassField;)V
- */
- JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessInstanceField
- (JNIEnv *, jclass, jobject);
- /*
- * Class: com_study_jnilearn_AccessField
- * Method: accessStaticField
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessStaticField
- (JNIEnv *, jclass);
- #ifdef __cplusplus
- }
- #endif
- #endif
- // AccessField.c
- #include "com_study_jnilearn_AccessField.h"
- /*
- * Class: com_study_jnilearn_AccessField
- * Method: accessInstanceField
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessInstanceField
- (JNIEnv *env, jclass cls, jobject obj)
- {
- jclass clazz;
- jfieldID fid;
- jstring j_str;
- jstring j_newStr;
- const char *c_str = NULL;
- // 1.获取AccessField类的Class引用
- clazz = (*env)->GetObjectClass(env,obj);
- if (clazz == NULL) {
- return;
- }
- // 2. 获取AccessField类实例变量str的属性ID
- fid = (*env)->GetFieldID(env,clazz,"str", "Ljava/lang/String;");
- if (clazz == NULL) {
- return;
- }
- // 3. 获取实例变量str的值
- j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
- // 4. 将unicode编码的java字符串转换成C风格字符串
- c_str = (*env)->GetStringUTFChars(env,j_str,NULL);
- if (c_str == NULL) {
- return;
- }
- printf("In C--->ClassField.str = %s\n", c_str);
- (*env)->ReleaseStringUTFChars(env, j_str, c_str);
- // 5. 修改实例变量str的值
- j_newStr = (*env)->NewStringUTF(env, "This is C String");
- if (j_newStr == NULL) {
- return;
- }
- (*env)->SetObjectField(env, obj, fid, j_newStr);
- // 6.删除局部引用
- (*env)->DeleteLocalRef(env, clazz);
- (*env)->DeleteLocalRef(env, j_str);
- (*env)->DeleteLocalRef(env, j_newStr);
- }
- /*
- * Class: com_study_jnilearn_AccessField
- * Method: accessStaticField
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessStaticField
- (JNIEnv *env, jclass cls)
- {
- jclass clazz;
- jfieldID fid;
- jint num;
- //1.获取ClassField类的Class引用
- clazz = (*env)->FindClass(env,"com/study/jnilearn/ClassField");
- if (clazz == NULL) { // 错误处理
- return;
- }
- //2.获取ClassField类静态变量num的属性ID
- fid = (*env)->GetStaticFieldID(env, clazz, "num", "I");
- if (fid == NULL) {
- return;
- }
- // 3.获取静态变量num的值
- num = (*env)->GetStaticIntField(env,clazz,fid);
- printf("In C--->ClassField.num = %d\n", num);
- // 4.修改静态变量num的值
- (*env)->SetStaticIntField(env, clazz, fid, 80);
- // 删除属部引用
- (*env)->DeleteLocalRef(env,clazz);
- }
运行程序,输出结果如下:
代码解析:
一、访问实例变量
在main方法中,通过调用accessInstanceField()方法来调用本地函数Java_com_study_jnilearn_AccessField_accessInstanceField,定位到函数32行:
- j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
- jobject (JNICALL *GetObjectField) (JNIEnv *env, jobject obj, jfieldID fieldID);
GetObjectField函数接受3个参数,env是JNI函数表指针,obj是实例变量所属的对象,fieldID是变量的ID(也称为属性描述符或签名),和上一章中方法描述符是同一个意思。env和obj参数从Java_com_study_jnilearn_AccessField_accessInstanceField函数形参列表中可以得到,那fieldID怎么获取呢?了解Java反射的童鞋应该知道,在Java中任何一个类的.class字节码文件被加载到内存中之后,该class子节码文件统一使用Class类来表示该类的一个引用(相当于Java中所有类的基类是Object一样)。然后就可以从该类的Class引用中动态的获取类中的任意方法和属性。注意:Class类在Java SDK继承体系中是一个独立的类,没有继承自Object。请看下面的例子,通过Java反射机制,动态的获取一个类的私有实例变量的值:
- public static void main(String[] args) throws Exception {
- ClassField obj = new ClassField();
- obj.setStr("YangXin");
- // 获取ClassField字节码对象的Class引用
- Class<?> clazz = obj.getClass();
- // 获取str属性
- Field field = clazz.getDeclaredField("str");
- // 取消权限检查,因为Java语法规定,非public属性是无法在外部访问的
- field.setAccessible(true);
- // 获取obj对象中的str属性的值
- String str = (String)field.get(obj);
- System.out.println("str = " + str);
- }
- clazz = (*env)->GetObjectClass(env,obj);
然后调用GetFieldID函数从Class引用中获取字段的ID(str是字段名,Ljava/lang/String;是字段的类型)
- fid = (*env)->GetFieldID(env,clazz,"str", "Ljava/lang/String;");
- j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
- (*env)->SetObjectField(env, obj, fid, j_newStr);
二、访问静态变量
访问静态变量和实例变量不同的是,获取字段ID使用GetStaticFieldID,获取和修改字段的值使用Get/SetStaticXXXField系列函数,比如上例中获取和修改静态变量num:
- // 3.获取静态变量num的值
- num = (*env)->GetStaticIntField(env,clazz,fid);
- // 4.修改静态变量num的值
- (*env)->SetStaticIntField(env, clazz, fid, 80);
总结:
1、由于JNI函数是直接操作JVM中的数据结构,不受Java访问修饰符的限制。即,在本地代码中可以调用JNI函数可以访问Java对象中的非public属性和方法
2、访问和修改实例变量操作步聚:
1>、调用GetObjectClass函数获取实例对象的Class引用
2>、调用GetFieldID函数获取Class引用中某个实例变量的ID
3>、调用GetXXXField函数获取变量的值,需要传入实例变量所属对象和变量ID
4>、调用SetXXXField函数修改变量的值,需要传入实例变量所属对象、变量ID和变量的值
3、访问和修改静态变量操作步聚:
1>、调用FindClass函数获取类的Class引用
2>、调用GetStaticFieldID函数获取Class引用中某个静态变量ID
3>、调用GetStaticXXXField函数获取静态变量的值,需要传入变量所属Class的引用和变量ID
4>、调用SetStaticXXXField函数设置静态变量的值,需要传入变量所属Class的引用、变量ID和变量的值
示例代码下载地址:https://code.csdn.net/xyang81/jnilearn
- JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
- JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
- JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
- JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
- JNI/NDK开发指南(六)--C/C++访问Java实例变量和静态变量
- JNI/NDK开发指南(六)——C/C++访问Java实例方法和静态方法
- JNI/NDK开发指南(六)——C/C++访问Java实例方法和静态方法
- JNI/NDK开发指南(六)——C/C++访问Java实例方法和静态方法
- JNI/NDK开发指南(五)---C/C++访问Java实例方法和静态方法
- (JNI)C/C++ 访问 Java 实例变量和静态变量
- C/C++ 访问 Java 实例变量和静态变量
- Android JNI开发(4)--访问Java的实例变量和静态变量
- JNI:在C/C++中访问静态成员变量变量(Accessing Static Fields)
- Android NDK 学习之在C中调用Java的变量和静态变量
- JNI/NDK开发指南(七)---调用构造方法和父类实例方法
- JNI/NDK开发指南(七)
- JNI/NDK开发指南(八)——调用构造方法和父类实例方法
- JNI/NDK开发指南(八)——调用构造方法和父类实例方法
- Java EE 开发环境搭建
- 八皇后问题
- nil和 null和NSNull的区别及相关问题
- 你所不知道的Activity转场动画——ActivityOptions
- java.util.logging.Logger中log()
- JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量
- kafka源码解析之十五客户端如何创建topic
- Android中ExpandableListView控件基本使用
- DWM1000DISCOVERY配置角色(Anchor或者Tag)和地址的方法
- 使用Express框架应用生成器快速搭建一个应用骨架
- Android V7包学习笔记更新中.....
- JavaSe基础(26)-- File类
- 【poj 1691】Painting A Board 题意&题解&代码(C++)
- 可以用Navicat Cloud做什么?