调用构造方法和父类实例方法

转载请注明出处:

在第6章我们学习到了在Native层如何调用Java静态方法和实例方法,其中调用实例方法的示例代码中也提到了调用构造函数来实始化一个对象,但没有详细介绍,一带而过了。还没有阅读过的同学请移步《JNI/NDK开发指南(六)——C/C++访问Java实例方法和静态方法》阅读。这章详细来介绍下初始一个对象的两种方式,以及如何调用子类对象重写的父类实例方法。

我们先回过一下,在Java中实例化一个对象和调用父类实例方法的流程。先看一段代码:

package com.study.jnilearn;{() {System.out.println(“Animal.run…”);}}package com.study.jnilearn;{() {System.out.println(name + ” Cat.run…”);}}(String[] args) {Animal cat = new Cat(“汤姆”);cat.run();}

正如你所看到的那样,上面这段代码非常简单,有两个类Animal和Cat,Animal类中定义了run和getName两个方法,Cat继承自Animal,并重写了父类的run方法。在main方法中,首先定义了一个Animal类型的变量cat,并指向了Cat类的实例对象,然后调用了它的run方法。在执行new Cat(“汤姆”)这段代码时,会先为Cat类分配内存空间(所分配的内存空间大小由Cat类的成员变量数量决定),然后调用Cat的带参构造方法初始化对象。 cat是Animal类型,但它指向的是Cat实例对象的引用,而且Cat重写了父类的run方法,因为调用run方法时有多态存在,所以访问的是Cat的run而非Animal的run,运行后打印的结果为:汤姆 Cat.run… 如果要调用父类的run方法,只需在Cat的run方法中调用super.run()即可,相当的简单。

写过C或C++的同学应该都有一个很深刻的内存管理概念,栈空间和堆空间,栈空间的内存大小受操作系统限制,由操作系统自动来管理,速度较快,所以在函数中定义的局部变量、函数形参变量都存储在栈空间。操作系统没有限制堆空间的内存大小,只受物理内存的限制,内存需要程序员自己管理。在C语言中用malloc关键字动态分配的内存和在C++中用new创建的对象所分配内存都存储在堆空间,内存使用完之后分别用free或delete/delete[]释放。这里不过多的讨论C/C++内存管理方面的知识,有兴趣的同学请自行百度。做Java的童鞋众所周知,写Java程序是不需要手动来管理内存的,内存管理那些烦锁的事情全都交由一个叫GC的线程来管理(当一个对象没有被其它对象所引用时,该对象就会被GC释放)。但我觉得Java内部的内存管理原理和C/C++是非常相似的,上例中,Animal cat = new Cat(“汤姆”); 局部变量cat存放在栈空间上,new Cat(“汤姆”);创建的实例对象存放在堆空间,返回一个内存地址的引用,存储在cat变量中。这样就可以通过cat变量所指向的引用访问Cat实例当中所有可见的成员了。

所以创建一个对象分为2步: 1. 为对象分配内存空间 2. 初始化对象(调用对象的构造方法)

下面通过一个示例来了解在JNI中是如何调用对象构造方法和父类实例方法的。为了让示例能清晰的体现构造方法和父类实例方法的调用流程,定义了Animal和Cat两个类,Animal定义了一个String形参的构造方法,一个成员变量name、两个成员函数run和getName,Cat继承自Animal,并重写了run方法。在JNI中实现创建Cat对象的实例,调用Animal类的run和getName方法。代码如下所示:

// Animal.javapackage com.study.jnilearn;{protected String name;public Animal(String name) {this.name = name;System.out.println(“Animal Construct call…”);}public String getName() {System.out.println(“Animal.getName Call…”);return this.name;}() {System.out.println(“Animal.run…”);} }// Cat.javapackage com.study.jnilearn;{public Cat(String name) {super(name);System.out.println(“Cat Construct call….”);}@Overridepublic String getName() {return “My name is ” + this.name;}() {System.out.println(name + ” Cat.run…”);}}// AccessSuperMethod.javapackage com.study.jnilearn;{();(String[] args) {callSuperInstanceMethod();}static {System.loadLibrary(“AccessSuperMethod”);}}

AccessSuperMethod类是程序的入口,其中定义了一个native方法callSuperInstanceMethod。用javah生成的jni函数原型如下:

extern “C” {#endif/* * Class:com_study_jnilearn_AccessSuperMethod * Method: callSuperInstanceMethod * Signature: ()V */JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessSuperMethod_callSuperInstanceMethod (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif

实现Java_com_study_jnilearn_AccessSuperMethod_callSuperInstanceMethod函数,如下所示:

// AccessSuperMethod.c#include “com_study_jnilearn_AccessSuperMethod.h”JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessSuperMethod_callSuperInstanceMethod (JNIEnv *env, jclass cls){jclass cls_cat;jclass cls_animal;jmethodID mid_cat_init;jmethodID mid_run;jmethodID mid_getName;jstring c_str_name;jobject obj_cat;const char *name = NULL;= (*env)->FindClass(env, “com/study/jnilearn/Cat”);if (cls_cat == NULL) {return;}// 2、获取Cat的构造方法ID(构造方法的名统一为:<init>)mid_cat_init = (*env)->GetMethodID(env, cls_cat, “<init>”, “(Ljava/lang/String;)V”);if (mid_cat_init == NULL) {return; // 没有找到只有一个参数为String的构造方法}// 3、创建一个String对象,作为构造方法的参数c_str_name = (*env)->NewStringUTF(env, “汤姆猫”);if (c_str_name == NULL) {return; // 创建字符串失败(内存不够)}// 4、创建Cat对象的实例(调用对象的构造方法并初始化对象)obj_cat = (*env)->NewObject(env,cls_cat, mid_cat_init,c_str_name);if (obj_cat == NULL) {return;}//————– 5、调用Cat父类Animal的run和getName方法 ————–cls_animal = (*env)->FindClass(env, “com/study/jnilearn/Animal”);if (cls_animal == NULL) {return;}// 例1: 调用父类的run方法mid_run = (*env)->GetMethodID(env, cls_animal, “run”, “()V”); // 获取父类Animal中run方法的idif (mid_run == NULL) {return;}// 注意:obj_cat是Cat的实例,cls_animal是Animal的Class引用,mid_run是Animal类中的方法ID(*env)->CallNonvirtualVoidMethod(env, obj_cat, cls_animal, mid_run);// 例2:调用父类的getName方法// 获取父类Animal中getName方法的idmid_getName = (*env)->GetMethodID(env, cls_animal, “getName”, “()Ljava/lang/String;”);if (mid_getName == NULL) {return;}c_str_name = (*env)->CallNonvirtualObjectMethod(env, obj_cat, cls_animal, mid_getName);name = (*env)->GetStringUTFChars(env, c_str_name, NULL);printf(“In C: Animal Name is %s\n”, name);// 释放从java层获取到的字符串所分配的内存(*env)->ReleaseStringUTFChars(env, c_str_name, name);quit:// 删除局部引用(jobject或jobject的子类才属于引用变量),,允许VM释放被局部变量所引用的资源(*env)->DeleteLocalRef(env, cls_cat);(*env)->DeleteLocalRef(env, cls_animal);(*env)->DeleteLocalRef(env, c_str_name);(*env)->DeleteLocalRef(env, obj_cat);}运行结果:

代码讲解 – 调用构造方法一个人目睹沿途的风景,拿着相机,拍下沿途上的风景,

调用构造方法和父类实例方法

相关文章:

你感兴趣的文章:

标签云: