仅供自己学习使用!
创建Employee类: Employee.java
public class Employee{
private String name;
private double salary;
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
public native void raiseSalary(double byPercent);
public void print(){
System.out.println(name + " " + salary);
}
static{
System.loadLibrary("Employee");
}
}
对Employee.java进行编译:
$ javac Employee.java
生成Employee.h的头文件:
$ javah Employee
得到Employee.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Employee */
#ifndef _Included_Employee
#define _Included_Employee
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Employee
* Method: raiseSalary
* Signature: (D)V
*/
JNIEXPORT void JNICALL Java_Employee_raiseSalary
(JNIEnv *, jobject, jdouble); /* 这里的第二个参数是jobject而不是先前的jclass,是因为静态方法得到的是类的引用,非静态方法得到的是隐含的this参数对象的引用 */
#ifdef __cplusplus
}
#endif
#endif
JNI要求通过特殊的JNI函数来获取和设置Employee类中的变量salary的值
由于salary是double类型的,因此可以使用GetDoubleField和SetDoubleField函数,类似的还有GetIntField/SetIntField等等,其语法如下:
x = (*env)->GetXxxField(env, this_obj, fieldID);
(*env)->SetXxxField(env, this_obj, fieldID, x);
有两种方法得到class对象:
1. jclass class_Employee = (*env)->GetObjectClass(JNIEnv *env, jobject obj); 通过对象获取这个类,可以返回任意对象的类
2. jclass class_String = (*env)->FindClass(env, "java/lang/String"); 可以通过字符串的形式来制定类名
fieldID 是一个特殊类型的值,jfieldID表示结构中的一个域(域可以理解为一个对象的字段,属性或者方法)
jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D"); 获取实例对象的域ID, salary是域的名称,D是域的类型(double)
需要注意的是,非静态的实例化后的对象,可能产生的异常有
1) NoSuchFieldError 找不到指定的域
2) ExceptionInInitializerError 因为异常而导致类初始化失败
3) OutOfMemoryError内存不足。
纵观所述: Employee.c的代码是:
#include "Employee.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv * env, jobject this_obj, jdouble byPercent)
{
/* 通过类获得 salary 的fieldID */
jclass class_Employee = (*env)->GetObjectClass(env, this_obj);
/* 通过类获得 salary 的fieldID */
jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D");
/* 获得引用对象中 salary 的值 */
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);
}
其中“通过类获得salary的fieldID” 比较的不好理解,虽然我不知道这个具体是什么意思,但我把这个fieldID理解为地址偏移量,这个偏移量只能是针对类的,而不是针对具体的对象------例如我们假设偏移量是100, 现在又2个对象 obj1和obj2, obj1和obj2都是 Employee的对象,然而他们的起始地址肯定是不同的,如何才能得到这两个对象中salary的值呢?程序只需要知道这两个对象的起始地址就可以了(而这个地址,程序肯定是知道的) 比如obj1的其实地址为10000,那么要引用obj1的salary变量只需要10000+100即可,obj2的起始地址是20000,则obj2的salary只需要20000+100就可以引用到!(个人方便理解)
测试是否通过:TestEmployee.java
public class TestEmployee{
public static void main(String[] args){
Employee[] staff = new Employee[3];
staff[0] = new Employee("Harry", 35000);
staff[1] = new Employee("Carl", 75000);
staff[2] = new Employee("Potter", 38000);
for (Employee e : staff)
e.raiseSalary(5);
for (Employee e : staff)
e.print();
}
}
刚才在Employee.c中提到的this_obj 是对象的引用,这里在new出来3个对象即是this_obj的引用