您的位置:首页 > 编程语言 > Java开发

java jni 入门4 - 访问实例域,静态域

2015-11-11 14:22 585 查看
参考:《Java核心技术 卷II:高级特性》第12章 本地方法

######################################################################

之前使用的本地方法都是带有数字或字符串参数的静态方法。下面考虑操作对象的本地方法。

访问实例域:

一个java函数:

public void raiseSalary(double byPercent) {
salary += 1+ byPercent / 100;
}
变量salary为全局变量

重写代码,使其成为一个本地方法。与此前的本地方法不同,它并不是一个静态方法。

运行javah给出以下原型:

JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv *, jobject, jdouble)
注意,第二个参数不再是jclass类型而是jobject类型。实际上,它和this等价。静态方法得到的是类的引用,而非静态方法的得到的是隐含的this参数对象的引用

在Java1.0中“原始的”Java到C的绑定中,可以直接访问对象数据域。然而,直接访问要求虚拟机暴露它们的内部数据布局。基于这个原因,JNI要求程序员通过调用特殊的JNI函数来获取和设置数据的值

本地示例中,使用GetDoubleField和SetDoubleField函数,因为salary是double类型的。对于其他类型,可以使用的函数有:GetIntField / SetIntField , GetObjectField / SetObjectField等等。其一般语法是:

x=(*env)->GetXxxField(env, this_obj, fieldID)
(*env)->SetXxxField(env, this_obj, fieldID, x)
这里,class代表Class类型Java对象的一个值,fieldID是一个特殊类型的值,jfield标识结构中的一个域,Xxx代表Java数据类型(Object, Boolean, Byte或其他)。

有两种方法得到class对象。

GetObjectClass函数返回任意对象的类。例如
jclass class_Empolyee = (*env)->GetObjectClass(env, this_obj)
FindClass函数可以以字符串形式来指定类名(要以反斜线/代替句号作为包名之间的分隔符)。例如

jclass class_String = (*env)->FindClass(env, "java/lang/String")


使用GetFieldID函数来获得fieldID。必须提供域的名字,它的签名以及它的类型的编码。例如,从salary域得到域ID的代码:
jfieldID id_salary=(*env)->GetFieldID(env, class_Employee, "salary", "D")
字符串"D"表示类型是double

JNI的设计者不想把数据域直接暴露在外,所以不得不提供获取和设置数据域值的函数。为了使这些函数的开销最小化,从域名计算域ID(代价最大的一个步骤)被分解出来作为单独的一步操作。也就是说,如果反复地获取和设置一个特定的域,计算域标识符的开销只有一次。

下面代码以本地方法形式重新实现了函数raiseSalary方法

JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv *env, jobject this_obj, jdouble byPercent) {
/* get the class */
jclass class_Employee = (*env)->GetObjectClass(env, this_obj);

/* get the field ID */
jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D");

/* get the field value */
jdouble salary = (*env)->GetDoubleField(env, this_obj, id_salary);

salary *= 1 + byPercent / 100;

/* set the field value */
(*env)->SetDoubleField(env, this_obj, id_salary, salary);
}


注意,类引用只在本地方法返回之前有效。因此,不能在代码中缓存GetObjectClass的返回值。不要将类引用保存下来以供以后的方法调用重复使用。在每次执行本地方法时必须都调用GetObjectClass。如果无法忍受这一点,必须调用NewGlobalRef来锁定该引用:

static jclass class_X = 0;
static jfieldID id_a;
......
if (class_x == 0)
{
jclass cx = (*env)->GetObjectClass(env, obj);
class_X = (*env)->NewGlobalRef(env, cx);
id_a = (*env)->GetFieldID(env, cls, "a", "...");
}


这样,就可以在后面的调用中使用类引用和域ID。当你结束对类的使用时,务必调用:(*env)->DeleteGlobalRef(env, Class_X);

以下是完整的测试程序和Employee类的java代码

EmployeeTest.java

/**
* @time 15-11-11
* @author zj
*/
public class EmployeeTest {
public static void main(String[] args) {
Employee[] staff = new Employee[3];
staff[0] = new Employee("Harry Hacker", 35000);
staff[1] = new Employee("Carl Cracker", 75000);
staff[2] = new Employee("Tony Tester", 38000);

for (Employee e : staff) {
e.raiseSalary(5);
}
for (Employee e : staff) {
e.print();
}
}
}


Employee.java

/**
* @time 15-11-11
* @author zj
*/
class Employee {
public Employee(String n, double s) {
name = n;
salary = s;
}

public native void raiseSalary(double byPercent);

public void print() {
System.out.println(name+" "+salary);
}

private String name;
private double salary;

static {
System.loadLibrary("Employee");
}
}


Employee.c

/**
* @time 15-11-11
* @author zj
*/
#include "Employee.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_Employee_raiseSalary
(JNIEnv *env, jobject this_obj, jdouble byPercent) {
/* get the class */
jclass class_Employee = (*env)->GetObjectClass(env, this_obj);

/* get the field ID */
jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D");

/* get the field value */
jdouble salary = (*env)->GetDoubleField(env, this_obj, id_salary);

salary *= 1 + byPercent/100;

/* set the field value */
(*env)->SetDoubleField(env, this_obj, id_salary, salary);
}




######################################################3

访问静态域

访问静态域和访问非静态域类似,只不过要使用GetStaticFieldID和GetStaticXxxField/SetStaticXxxFieldID函数。

只有两个区别:

1.由于没有对象,必须使用FindClass代替GetObjectClass来获得类引用。

2.访问域时,要提供类而非实例对象。

例如,下面给出如何得到System.out的引用的代码:

/* get the class */
jclass class_System = (*env)->FindClass(env, "java/lang/System");

/* get the field ID */
jfieldID id_out = (*env)->GetStaticFieldID(env, class_System, "out", "Ljava/io/PrintStream;");

/* get the field vlaue */
jobject obj_out = (*env)->GetStaticObjectField(env, class_System, id_out);


#######################################################################3

访问域相关函数:

jfieldID GetFieldID(JNIEnv *env, jclass cl, const char name[], const char fieldSignature[])
返回类中一个域的标识符

Xxx GetXxxField(JNIEnv *env, jobject obj, jfieldID id)
返回域的值。域类型Xxx是Object, Boolean, Byte, Char, Short, Int, Long, Float 或 Double之一

void setXxxField(JNIEnv *env, jobject obj, jfieldID id, Xxx value)
把某个域设置为一个新值。域类型Xxx是Object, Boolean, Byte, Char, Short, Int, Long, Float或Double之一

jfieldID GetStaticFieldID(JNIEnv *env, jclass cl, const char name[], const char fieldsignature[])
返回某类型的一个静态域的标识符

Xxx GetStaticXxxField(JNIEnv *env, jclass cl, jfieldID id)
返回某静态的值。域类型Xxx是Object, Boolean, Byte, Char, Short, Int, Long, Float或Double之一。

void SetStaticXxxField(JNIEnv *env, jclass cl, jfieldID id, Xxx value)
把某个静态域设置为一个新值。域类型Xxx是Object, Boolean, Byte, Char, Short, Int, Long, Float或Double之一
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: