- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
A.1.3 传递和使用 Java 对象
在前例中,我们将一个字串传递给固有方法。事实上,亦可将自己创建的 Java 对象传递给固有方法。
在我们的固有方法内部,可访问已收到的那些对象的字段及方法。
为传递对象,声明固有方法时要采用原始的 Java 语法。如下例所示,MyJavaClass 有一个 public(公共)字段,以及一个 public 方法。UseObjects 类声明了一个固有方法,用于接收 MyJavaClass 类的一个对象。为调查固有方法是否能控制自己的自变量,我们设置了自变量的 public 字段,调用固有方法,然后打印出 public 字段的值。
class MyJavaClass { public void divByTwo() { aValue /= 2; } public int aValue; } public class UseObjects { public static void main(String [] args) { UseObjects app = new UseObjects(); MyJavaClass anObj = new MyJavaClass(); anObj.aValue = 2; app.changeObject(anObj); System.out.println("Java: " + anObj.aValue); } private native void changeObject(MyJavaClass obj); static { System.loadLibrary("UseObjImpl"); } }
编译好代码,并将.class 文件传递给 javah 后,就可以实现固有方法。在下面这个例子中,一旦取得字段和方法 ID,就会通过 JNI 函数访问它们。
JNIEXPORT void JNICALL Java_UseObjects_changeObject( JNIEnv * env, jobject jThis, jobject obj) { jclass cls; jfieldID fid; jmethodID mid; int value; cls = env->GetObjectClass(obj); fid = env->GetFieldID(cls, "aValue", "I"); mid = env->GetMethodID(cls, "divByTwo", "()V"); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); env->SetIntField(obj, fid, 6); env->CallVoidMethod(obj, mid); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); }
除第一个自变量外,C++函数会接收一个 jobject,它代表 Java 对象引用“固有”的那一面——那个引用是我们从 Java 代码里传递的。我们简单地读取 aValue,把它打印出来,改变这个值,调用对象的 divByTwo() 方法,再将值重新打印一遍。
为访问一个字段或方法,首先必须获取它的标识符。利用适当的 JNI 函数,可方便地取得类对象、元素名以及签名信息。这些函数会返回一个标识符,利用它可访问对应的元素。尽管这一方式显得有些曲折,但我们的固有方法确实对 Java 对象的内部布局一无所知。因此,它必须通过由 JVM 返回的索引访问字段和方法。这样一来,不同的 JVM 就可实现不同的内部对象布局,同时不会对固有方法造成影响。
若运行 Java 程序,就会发现从 Java 那一侧传来的对象是由我们的固有方法处理的。但传递的到底是什么呢?是指针,还是 Java 引用?而且垃圾收集器在固有方法调用期间又在做什么呢?
垃圾收集器会在固有方法执行期间持续运行,但在一次固有方法调用期间,我们的对象可保证不会被当作“垃圾”收集去。为确保这一点,事先创建了“局部引用”,并在固有方法调用之后立即清除。由于它们的“生命期”与调用过程息息相关,所以能够保证对象在固有方法调用期间的有效性。
由于这些引用会在每次函数调用的时候创建和破坏,所以不可在 static 变量中制作固有方法的局部副本(本地拷贝)。若希望一个引用在函数存在期间持续有效,就需要一个全局引用。全局引用不是由 JVM 创建的,但通过调用特定的 JNI 函数,程序员可将局部引用扩展为全局引用。创建一个全局引用时,需对引用对象的“生存时间”负责。全局引用(以及它引用的对象)会一直留在内存里,直到用特定的 JNI 函数明确释放了这个引用。它类似于 C 的 malloc() 和 free()。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论