In the previous chapter, we learned how to access static methods and instance methods in any Java class in local code. In this chapter, we also learned how to access and modify instance variables and static variables in Java through an example. Static variables, also known as class variables (attributes), share the same data in all instance objects and can be accessed directly through [class name. Variable name]. Instance variables are also called member variables (attributes). Each instance has a copy of instance variable data, and the modified data between them does not affect each other. Here is an example:
package com.study.jnilearn; /** * C/C++Accessing instance variables and static variables of a class * @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"); // Local code accesses and modifies the static attribute num in ClassField accessStaticField(); accessInstanceField(obj); // Output the modified value of the local code System.out.println("In Java--->ClassField.num = " + obj.getNum()); System.out.println("In Java--->ClassField.str = " + obj.getStr()); } static { System.loadLibrary("AccessField"); } }
AccessField is the entry class of the program. It defines two native methods: accessInstanceField and accessStaticField, which are used to demonstrate accessing instance variables and static variables in Java classes in local code. The accessInstanceField method accesses the instance variables of the class, so the method requires a ClassField instance as a formal parameter to access the instance variables in the object.
package com.study.jnilearn; /** * ClassField.java * It is used for local code to access and modify the properties of this class * @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; } }
In this example, instance variables and static variables are not defined in the program entry class. A new ClassField class is created to define the properties of the class. The purpose is to deepen the access to the properties of any Java class in C/C + + code. This class defines an instance variable num of type int and a Java Static variable str of type lang.string. These two variables are accessed and modified by local code.
/* 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
The above code is the program entry class accessfield Class is the native code function prototype header file generated by the native method
// 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. Get the Class reference of AccessField Class clazz = (*env)->GetObjectClass(env,obj); if (clazz == NULL) { return; } // 2. Get the attribute ID of the AccessField class instance variable str fid = (*env)->GetFieldID(env,clazz,"str", "Ljava/lang/String;"); if (clazz == NULL) { return; } // 3. Get the value of the instance variable str j_str = (jstring)(*env)->GetObjectField(env,obj,fid); // 4. Convert unicode encoded java string into C-style string 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. Modify the value of the instance variable str j_newStr = (*env)->NewStringUTF(env, "This is C String"); if (j_newStr == NULL) { return; } (*env)->SetObjectField(env, obj, fid, j_newStr); // 6. Delete local references (*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. Get the Class reference of ClassField Class clazz = (*env)->FindClass(env,"com/study/jnilearn/ClassField"); if (clazz == NULL) { // error handling return; } //2. Get the attribute ID of the static variable num of ClassField class fid = (*env)->GetStaticFieldID(env, clazz, "num", "I"); if (fid == NULL) { return; } // 3. Get the value of the static variable num num = (*env)->GetStaticIntField(env,clazz,fid); printf("In C--->ClassField.num = %d\n", num); // 4. Modify the value of the static variable num (*env)->SetStaticIntField(env, clazz, fid, 80); // Delete subordinate reference (*env)->DeleteLocalRef(env,clazz); }
The above code is the implementation of the function prototype in the header file.
Run the program, and the output results are as follows:
Code parsing:
1, Access instance variables
In the main method, call the local Java function by calling the accessInstanceField() method_ com_ study_ jnilearn_ AccessField_ Accessinstancefield, navigate to line 32 of the function:
j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
This function is used to obtain the value of num in the ClassField object. The following is the prototype of the function:
jobject (JNICALL *GetObjectField) (JNIEnv *env, jobject obj, jfieldID fieldID);
Because the instance variable str is of String type and belongs to reference type. Get the value of the reference type field in JNI and call GetObjectField function to get it. Similarly, functions to obtain field values of other types include GetIntField, GetFloatField, GetDoubleField, getboolean field, etc. These functions have one thing in common. The function parameters are the same, but the function names are different. We only need to learn how to call one of them, and so on, and we will naturally know the use methods of other functions.
GetObjectField function accepts three parameters. env is the JNI function table pointer, obj is the Object to which the instance variable belongs, and fieldID is the ID of the variable (also known as attribute descriptor or signature), which has the same meaning as the method descriptor in the previous chapter. env and obj parameters from Java_ com_ study_ jnilearn_ AccessField_ The accessinstancefield function can be obtained from the formal parameter list. How to obtain the fieldID? Children's shoes that understand java reflection should know that any class in Java After the class bytecode file is loaded into memory, the class sub section code file uniformly uses the class class to represent a reference of the class (equivalent to that the base class of all classes in Java is Object). Then you can dynamically get any methods and properties in the class from the class reference of the class. Note: class is an independent class in the Java SDK inheritance system and does not inherit from Object. See the following example to dynamically obtain the value of the private instance variable of a class through the java reflection mechanism:
public static void main(String[] args) throws Exception { ClassField obj = new ClassField(); obj.setStr("YangXin"); // Gets the Class reference of the ClassField bytecode object Class<?> clazz = obj.getClass(); // Get str property Field field = clazz.getDeclaredField("str"); // The permission check is canceled because the Java syntax stipulates that non public attributes cannot be accessed externally field.setAccessible(true); // Gets the value of the str attribute in the obj object String str = (String)field.get(obj); System.out.println("str = " + str); }
After running the program, the output result is of course to print out the value of str attribute "YangXin". So when we call the JNI function in the local code to access an attribute in the Java object, the first step is to get the Class reference of the object, then find the field ID to be visited in Class, and finally call the GetXXXField series function of the JNI function to get the value of the word segment (attribute). In the above example, first call the GetObjectClass function to obtain the class reference of ClassField:
clazz = (*env)->GetObjectClass(env,obj);
Then the GetFieldID function is called to get the ID of the field from the Class reference (str is the field name, Ljava/lang/String, and the type of the field).
fid = (*env)->GetFieldID(env,clazz,"str", "Ljava/lang/String;");
Finally, the GetObjectField function is called to import the instance object and the field ID to get the value of the attribute.
j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
Call SetXXXField series functions to modify the value of the instance property. The last parameter is the value of the property. All reference types call SetObjectField function, and basic types call SetIntField, SetDoubleField, setboolean field, etc
(*env)->SetObjectField(env, obj, fid, j_newStr);
2, Accessing static variables
The difference between accessing static variables and instance variables is that GetStaticFieldID is used to obtain the field ID, and Get/SetStaticXXXField series functions are used to obtain and modify the field value. For example, in the above example, get and modify the static variable num:
// 3. Get the value of the static variable num num = (*env)->GetStaticIntField(env,clazz,fid); // 4. Modify the value of the static variable num (*env)->SetStaticIntField(env, clazz, fid, 80);
Summary:
1. Since JNI functions directly operate on data structures in the JVM, they are not limited by Java access modifiers. That is, JNI functions can be called in local code to access non-public properties and methods in Java objects
2. To access and modify instance variables:
1> . call the GetObjectClass function to get the Class reference of the instance object
2> . call the GetFieldID function to obtain the ID of an instance variable in the Class reference
3> . call the GetXXXField function to obtain the value of the variable. You need to pass in the object and variable ID of the instance variable
4> . call SetXXXField function to modify the value of the variable. You need to pass in the object to which the instance variable belongs, the variable ID and the value of the variable
3. Operation steps for accessing and modifying static variables:
1 > call FindClass function to get the Class reference of the Class
2> . call GetStaticFieldID function to get the ID of a static variable in the Class reference
3> . call GetStaticXXXField function to obtain the value of the static variable. You need to pass in the reference of the Class to which the variable belongs and the variable ID
4> . call SetStaticXXXField function to set the value of the static variable. You need to pass in the reference of the Class to which the variable belongs, the variable ID and the value of the variable
Sample code download address: https://code.csdn.net/xyang81/jnilearn