文章目录




I . 调用 Java 方法流程



JNI 中调用 Java 方法流程 :



① 获取 jclass 类型变量 :

调用 jclass GetObjectClass(jobject obj) 或 jclass FindClass(const char* name) 方法 , 获取 jclass 类型变量 ;

② 通过反射获取方法 :

调用 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) 获取 Java 对象方法 ,

调用 jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) 获取 Java 类的静态方法 ;

③ 调用方法 :

void CallVoidMethod(jobject obj, jmethodID methodID, …) 调用 Java 对象方法 ,

void CallStaticVoidMethod(jclass clazz, jmethodID methodID, …) 调用 Java 的静态方法 ;




CalXxxMethod 方法 , 其中的 Xxx 是 Java 对象的 返回值 , 不同的返回值调用不同的方法 ;





II . 获取 jclass 对象 ( GetObjectClass )



1 . 函数原型 :​ 通过传入 Java 对象 ( jobject 类型变量 ) , 获取 Java 类对象 ( jclass 类型变量 )

返回值 :​ 返回 Java 字节码 Class 对象 , 对应 C/C++ 中的 jclass 对象 ;

参数 :​ 传入 Java 对象 ; ( 该对象一般是由 JNI 方法传入的 )

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetObjectClass方法
jclass GetObjectClass(jobject obj)
{ return functions->GetObjectClass(this, obj); }
...
}



2 . 代码示例 :

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
//获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);
...
}




III . 获取 jclass 对象 ( FindClass )



函数原型 :​ 通过传入完整的 包名.类名 获取 Java 类对应的 C/C++ 环境下的 jclass 类型变量 ;

返回值 :​ 返回 Java 字节码 Class 对象 , 对应 C/C++ 中的 jclass 对象 ;

参数 :​ 传入 完整的 包名/类名 , 注意包名中使用 “/” 代替 “.” , 如 “kim/hsl/jni/Teacher” ;

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 FindClass 方法
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
...
}



2 . 代码示例 :​ 获取 kim.hsl.jni.Teacher 对应的 jclass 对象 ;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
// 5.2 获取 Teacher 类 ( 该变量需要释放 )
jclass class_teacher = env->FindClass("kim/hsl/jni/Teacher");
...
}




IV . JNI 函数签名规则




参考 : ​​JNI 函数签名规则​





V . javap 获取函数签名 ( 推荐 )




自己写函数签名容易出错 , 还麻烦 , 推荐使用 javap 工具 ;




1 . 字节码文件 :​ 首先要先编译出 Student 的 class 字节码文件 , javap 命令要直接作用于该字节码文件 ;



2 . Android Studio 中 Java 代码编译后的 class 字节码文件位置 :​ 不同版本的 AS 编译后的字节码位置不同 , 建议在各自的 Module 下的 build 目录中进行文件查找 , 找到 class 字节码所在目录 ;



3 . 我的 AS 中目录位置是 :​ Y:\002_WorkSpace\001_AS\001_NDK_Hello\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes , 在这个目录下执行 ​javap -s kim.hsl.jni.Student​ 命令 , 即可获取类中的函数签名 ;

【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )_FindClass



4 . javap 命令格式 : javap -s 完整包名.类名 ;


如 : 要获取 kim.hsl.jni.Student 类中的函数签名 , 使用 javap -s kim.hsl.jni.Student 命令 ;




5 . 执行命令 :​ 在 class 目录下执行 ​javap -s kim.hsl.jni.Student​ 命令 ;

Y:\002_WorkSpace\001_AS\001_NDK_Hello\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes>javap -s kim.hsl.jni.Student
Compiled from "Student.java"
public class kim.hsl.jni.Student {
public kim.hsl.jni.Student();
descriptor: ()V

public kim.hsl.jni.Student(int, java.lang.String);
descriptor: (ILjava/lang/String;)V

public int getAge();
descriptor: ()I

public void setAge(int);
descriptor: (I)V

public java.lang.String getName();
descriptor: ()Ljava/lang/String;

public void setName(java.lang.String);
descriptor: (Ljava/lang/String;)V
}




VI . 反射获取对象方法 ( GetMethodID )



函数原型 :​ 通过 jclass 对象 , 方法名称 , 和 方法签名 , 获取 Java 类对象对应的方法 ID 即 jmethodID 类型变量 ;

返回值 :​ Java 类对象对应的方法 ID ( jmethodID 类型变量 )

参数 :


  • jclass clazz :​ 要获取的 Java 对象方法对应的 Java 类对象 ;
  • const char* name :​ 方法名称 ;
  • const char* sig :​ 方法签名 , 使用 javap 命令获得 ;

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetMethodID 方法
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }
...
}



2 . 代码示例 :​ 获取 Student 类的 getAge 方法 ;

student_class 是 Student 类对应的 C/C++ 中的 jclass 类型变量 ,

“getAge” 是方法名称 ,

“()I” 是方法签名 , 左侧的括号是参数列表类型签名 , 括号右的 I 是返回值类型 int ;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
//1 . 获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);

//2 . 获取 Student 的 public int getAge() 方法
jmethodID method_getAge = env->GetMethodID(student_class, "getAge" , "()I");
...
}




VII . 反射获取类静态方法 ( GetStaticMethodID )



函数原型 :​ 通过 jclass 对象 , 方法名称 , 和 方法签名 , 获取 Java 类对象对应的方法 ID 即 jmethodID 类型变量 ;

返回值 :​ Java 类对象对应的方法 ID ( jmethodID 类型变量 )

参数 :


  • jclass clazz :​ 要获取的 Java 对象方法对应的 Java 类对象 ;
  • const char* name :​ 方法名称 ;
  • const char* sig :​ 方法签名 , 使用 javap 命令获得 ;

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetStaticMethodID 方法
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticMethodID(this, clazz, name, sig); }
...
}



2 . 代码示例 :​ 获取 Student 类的 getAge 方法 ;

student_class 是 Student 类对应的 C/C++ 中的 jclass 类型变量 ,

“logInfo” 是方法名称 ,

“(Ljava/lang/String;)V” 是方法签名 , 左侧的括号是参数列表类型签名 , 括号右的 V 是返回值类型 void ;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
//1 . 获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);

//2 . 获取 Student 的 public static void logInfo(String info) 方法
// 注意这里要使用 GetStaticMethodID 方法反射该静态方法
jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V");
...
}




VIII . 调用 Java 对象方法 ( CallXxxMethod )




注意 : 返回值和参数必须 都是 Java 类型 ;


函数原型 :​ 通过 Java 对象 , Java 方法 ID , 及根据函数签名传入的 参数列表 ( 可变参数 ) , 反射调用该 Java 对象的方法 ;

返回值 :​ Void , 注意这里的返回值可以是 8 种 基本数据类型 , jboolean , jbyte , jshort 等类型 , 也可以是引用类型 jobject 类型 , ​只有这 10 种返回类型 , 没有其它类型​ ; ​( 注意 : 返回值 都是 Java 类型 )

参数 :


  • jobject obj :​ 要获取的 Java 对象方法对应的 Java 类对象 ;
  • jmethodID methodID :​ 方法 ID ;
  • … :​ 可变参数 , 方法的参数 ​( 注意 : 参数 必须 都是 Java 类型 )​ ;

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 CallVoidMethod 方法
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallVoidMethodV(this, obj, methodID, args);
va_end(args);
}
...
}



2 . 所有 Java 方法调用返回值类型 :

jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);



3 . 代码示例 :​ 调用 Student 类的 getAge 方法 ;

student 是 Student 类对象 ,

“method_getAge” 是方法 ID

由于没有参数 , 因此后面的可变参数列表为空 ;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
//1 . 获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);

//2 . 获取 Student 的 public static void logInfo(String info) 方法
// 注意这里要使用 GetStaticMethodID 方法反射该静态方法
jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V");

//3 . 调用 Student 的 public int getAge() 方法
jint age = env->CallIntMethod(student, method_getAge);
...
}




IX . 调用 Java 类静态方法 ( CallStaticXxxMethod )




注意 : 返回值和参数必须 都是 Java 类型 ;


函数原型 :​ 通过 Java 类对象 ( Class 对象 对应 C/C++ jclass 类型对象 ) , Java 方法 ID , 及根据函数签名传入的 参数列表 ( 可变参数 ) , 反射调用该 Java 对象的方法 ;

返回值 :​ Void , 注意这里的返回值可以是 8 种 基本数据类型 , jboolean , jbyte , jshort 等类型 , 也可以是引用类型 jobject 类型 , ​只有这 10 种返回类型 , 没有其它类型​ ; ​( 注意 : 返回值 都是 Java 类型 )

参数 :


  • jobject obj :​ 要获取的 Java 对象方法对应的 Java 类对象 ;
  • jmethodID methodID :​ 方法 ID ;
  • … :​ 可变参数 , 方法的参数 ​( 注意 : 参数 必须 都是 Java 类型 )​ ;

struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 CallStaticVoidMethodV 方法
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallStaticVoidMethodV(this, clazz, methodID, args);
va_end(args);
}
...
}



2 . 所有 Java 方法调用返回值类型 :

jobject     (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
va_list);
jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);



3 . 代码示例 :​ 调用 Student 类的 logInfo 方法 ;

student 是 Student 类对象 ,

“method_logInfo” 是方法 ID

info :​ jstring 类型参数 , 传入字符串到 Java 层运行 ​( 注意 : 参数 必须 都是 Java 类型 )​ ;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
//1 . 获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);

//2 . 获取 Student 的 public static void logInfo(String info) 方法
// 注意这里要使用 GetStaticMethodID 方法反射该静态方法
jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V");

//3 . 准备传入的 jstring 参数
jstring info = env->NewStringUTF("C/C++ 创建的 Java 字符串");

//4 . 调用静态方法 : 注意传入的参数必须都是 Java 类型的参数
env->CallStaticVoidMethod(student_class, method_logInfo, info);
...
}




X . 完整代码示例



完整代码示例 :

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {

/*
参数解析 :
JNIEnv *env : JNI 环境 , 结构体指针 , 结构体中封装了 209 个方法
jobject instance : 是 MainActivity 对象
jobject student : Java 层创建的 Student 对象 , 传入 Native 层
*/

//在 C/C++ 中调用 Student 对象的 get 方法

//1 . 获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);

//2 . 通过 Class 的反射获取要调用的方法

/*
函数原型 :
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }

参数说明 :
jclass clazz : 使用 GetObjectClass 方法获取的返回值
const char* name : 要调用的方法名称
const char* sig : 函数签名 , 具体的签名规则查看签名表格

public int getAge() 函数签名 : ()I
左侧的 () : 左侧的括号是参数列表类型签名
右侧的 I : 括号右的 I 是返回值类型 int

public void setAge(int age) 函数签名 : (I)V
(I) 表示左侧的参数列表
右侧的 V 表示其返回值类型是 void 类型

引用类型签名 : L + 全限定名 + ;


javap 工具 :
可以使用 javap 工具获取方法签名

*/

//获取 Student 的 public int getAge() 方法
jmethodID method_getAge = env->GetMethodID(student_class, "getAge" , "()I");

//获取 Student 的 public void setAge(int age) 方法
jmethodID method_setAge = env->GetMethodID(student_class, "setAge" , "(I)V");

//获取 Student 的 public static void logInfo(String info) 方法
// 注意这里要使用 GetStaticMethodID 方法反射该静态方法
jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V");



//3 . 调用 Java 对象的方法

/*
调用 Java 引用对象的方法 : 要根据 返回值类型不同 , 调用不同的方法

如果返回值是 int 类型 , 那么就需要调用 CallIntMethod 方法
如果返回值是 void 类型 , 那么就需要调用 CallVoidMethod 方法

如果调用的是静态方法 , 那么需要调用

( 注意 : 调用方法时传入的参数都必须是 C/C++ 中的 Java 类型参数 , 如 jint , jstring 等 )

*/

//调用 Student 对象的 public void setAge(int age) 方法
env->CallVoidMethod(student, method_setAge, 18);

//调用 Student 的 public int getAge() 方法
jint age = env->CallIntMethod(student, method_getAge);


/*
调用静态方法 :
1 . 创建 Java 字符串
2 . 调用静态方法
3 . 释放 Java 字符串
*/

// 创建 Java 字符串
jstring info = env->NewStringUTF("C/C++ 创建的 Java 字符串");

// 调用静态方法 : 注意传入的参数
env->CallStaticVoidMethod(student_class, method_logInfo, info);

// jstring info 在方法中创建新的字符串 , 需要在方法结束之前释放该引用对象
env->DeleteLocalRef(info);


//4 . 设置 Student 对象属性

/*
反射获取属性

函数原型 :
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
{ return functions->GetFieldID(this, clazz, name, sig); }

参数说明 :
jclass clazz : Java 类对象
const char* name : 属性名称
const char* sig : 属性类型签名

设置反射属性值 :

函数原型 :
void SetIntField(jobject obj, jfieldID fieldID, jint value)
{ functions->SetIntField(this, obj, fieldID, value); }

参数说明 :
jobject obj : 设置对象
jfieldID fieldID : 通过 GetFieldID 方法获取的属性 ID
jint value : 要设置的值

注意 : 设置不同类型的值 , 调用不同的设置方法

*/
jfieldID age_field_id = env->GetFieldID(student_class, "age", "I");
env->SetIntField(student, age_field_id, 90);

// 验证是否设置成功
age = env->CallIntMethod(student, method_getAge);


//5 . 在 JNI 中创建 java 对象 , 并设置给另一个对象

/*
获取 Teacher 类 : 通过调用 FindClass 方法获取 Teacher 类
目前已知两种获取 jclass 的方法


获取 Teacher 类的构造方法 public Student(int age, String name)
构造方法的方法名都是 "<init>"
构造方法的函数签名为

此处还要特别注意 : 传入到 Java 方法中的参数 , 必须都是 Java 参数类型
如 jstring , jint , jintArray 等类型 , 不能将 C/C++ 类型传入参数
尤其是 char* 字符串 , 需要使用 NewStringUTF 将 C/C++ 字符串转为 jstring 类型字符串

创建 Teacher 对象

将 Teacher 对象设置给 Student 对象

*/

// 5.1 获取 Student 的 public void setTeacher(Teacher teacher) 方法
// 注意这个 Teacher 的类型签名是 Lkim/hsl/jni/Teacher;
jmethodID method_setTeacher = env->GetMethodID(student_class, "setTeacher" , "(Lkim/hsl/jni/Teacher;)V");

LOGE("method_setTeacher");

// 5.2 获取 Teacher 类 ( 该变量需要释放 )
jclass class_teacher = env->FindClass("kim/hsl/jni/Teacher");

// 5.3 查找构造方法
jmethodID method_init = env->GetMethodID(class_teacher, "<init>", "(ILjava/lang/String;)V");


// 5.4 准备 Java 类型参数 ( 该变量需要释放 )
// 此处特别注意 : 传入到 Java 方法中的参数都必须是 Java 参数
jint teacher_age = 88;
jstring teacher_name = env->NewStringUTF("Tom Wang");

// 5.5 创建 Teacher 对象 ( 该变量需要释放 )
jobject teacher = env->NewObject(class_teacher, method_init, teacher_age, teacher_name);

// 5.6 调用 Student 对象的 setTeacher 设置方法
env->CallVoidMethod(student, method_setTeacher, teacher);

// 5.7 释放上面通过 FindClass NewStringUTF NewObject 创建的引用变量 , 便于节省内存 , 也可以等到 作用域结束 自动释放
// 使用完这三个引用之后 , 不再使用 ; 这里特别建议手动释放三个引用
// 如果不手动释放 , 在 该引用 作用域 结束后 , 也会自动释放掉
env->DeleteLocalRef(teacher_name);
env->DeleteLocalRef(teacher);
env->DeleteLocalRef(class_teacher);


}