要正确地访问类对象的成员属性(字段)及成员方法,最重要的一点是一定要给出正确的签名,在Java中对于数据类型和方法的签名有如下的约定: 
| 
 数据类型/方法  | 
 签名  |  
| 
 byte  | 
 B  |  
| 
 char  | 
 C  |  
| 
 double  | 
 D  |  
| 
 float  | 
 F  |  
| 
 int  | 
 I  |  
| 
 long  | 
 J (注意:是J不是L)  |  
| 
 short  | 
 S  |  
| 
 void  | 
 V  |  
| 
 boolean  | 
 Z(注意:是Z不是B)  |  
| 
 类类型  | 
 L跟完整类名,如Ljava/lang/String; (注意:以L开头,要包括包名,以斜杠分隔,最后有一个分号作为类型表达式的结束)  |  
| 
 数组type[]  | 
 [type,例如 float[]的签名就是[float,如果是二维数组,如float[][],则签名为[[float,(注意:这里是两个 [ 符号)。  |  
| 
 方法  | 
 (参数类型签名)返回值类型签名,例如方法: float fun(int a,int b),它的签名为(II)F,(注意:两个I之间没有逗号!),而对于方法String toString(),则是()Ljava/lang/String;。  |   
通过上面的例子,我们了解了访问对象参数的成员属性或方法的基本步骤和多个Get方法的使用。TJNIEnv同时提供了多个Set方法,可以修改传入的对象参数的字段值,因为Java对象参数都是以传址的方式进行传递的,所以修改的结果可以在Java程序中得到反映。TJNIEnv提供的Get/Set方法,都需要两个基本参数:对象实例(JObject类型)和字段ID(JField类型),就可以根据提供的对象和字段ID来获取或设置这个对象的这个字段的值。 
现在我们了解了在Delphi代码中使用以及修改Java对象的操作步骤。进一步,如果需要在Delphi中从无到有地创建一个新的Java对象,可以吗?再来看一个例子,在Delphi中创建Java类的实例,操作方法其实也非常简单。 
先在Java代码中增加一个本地方法,如下: 
| 
  public native Book findBook(String t);  |   
然后,修改Delphi代码,增加一个函数(因为有返回值,所以不再是过程而是函数了): 
| 
 function Java_HelloWorld_findBook(PEnv: PJNIEnv; Obj: JObject; t:JString):JObject; stdcall; 
var 
 JVM: TJNIEnv; 
 c: JClass; 
 fid:JFieldID; 
 b:JObject; 
 mid:JMethodID; 
begin 
  JVM := TJNIEnv.Create(PEnv); 
  
  c:=JVM.FindClass('Book'); 
  mid:=JVM.GetMethodID(c,'<init>','()V'); 
  b:=JVM.NewObjectV(c,mid,nil); 
  fid:=JVM.GetFieldID(c,'title','Ljava/lang/String;'); 
  JVM.SetObjectField(b,fid,t); 
  fid:=JVM.GetFieldID(c,'price','D'); 
  JVM.SetDoubleField(b,fid,99.8); 
  Result:=b; 
  
  JVM.Free; 
end;  |   
这里先用FindClass方法根据类名查找到类,然后获取构造函数的方法ID,构造函数名称固定为“<init>”,注意签名为“()V”说明使用了Book类的一个空的构造函数。然后就是使用方法NewObjectV根据类和构造函数的方法ID来创建类的实例。创建了类实例,再对它进行操作就与前面的例子没有什么两样了。对于非空的构造函数,则略为复杂一点。需要设置它的参数表。还是上面的例子,在Book类中增加一个非空构造函数: 
| 
 public Book(Strint t,double p){ 
 this.title=t; 
this.price=p; 
}  |   
在Delphi代码中,findBook函数修改获取方法ID的代码如下: 
| 
 mid:=JVM.GetMethodID(c,'<init>','(Ljava/lang/String;D)V');  |   
构造函数名称仍是“<init>”,方法签名表示它有两个参数,分别是String和double。然后就是参数的传入了,在Delphi调用Java对象的方法如果需要传入参数,都需要构造出一个参数数组。在变量声明中加上: 
| 
 args : array[0..1] of JValue;  |   
注意!参数都是JValue类型,不管它是基本数据类型还是对象,都作为JValue的数组来处理。在代码实现中为参数设置值,并将数组的地址作为参数传给NewObjectA方法: 
| 
   args[0].l:=t; // t是传入的JString参数 
  args[1].d:=9.8; 
  
  b:=JVM.NewObjectA(c,mid,@args);  |   
为JValue类型的数据设置值的语句有点特殊,是吧?我们打开jni.pas,查看一下JValue的定义,原来它是一个packed record,已经包括了多种数据类型,JValue的定义如下: 
| 
   JValue = packed record 
  case Integer of 
    0: (z: JBoolean); 
    1: (b: JByte   ); 
    2: (c: JChar   ); 
    3: (s: JShort  ); 
    4: (i: JInt    ); 
    5: (j: JLong   ); 
    6: (f: JFloat  ); 
    7: (d: JDouble ); 
    8: (l: JObject ); 
  end;  |   
下面再来看一下错误处理,在调试前面的例子中,大家也许看到了一旦在Delphi的执行过程中发生了错误,控制台就会输出一大堆错误信息,如果想要屏蔽这些信息,也就是说希望在Delphi中捕获错误并直接处理它,应该怎么做?也很简单,在TJNIEnv中提供了两个方法可以方便地处理在访问Java对象时发生的错误。 
| 
 var 
… … 
ae:JThrowable; 
begin 
… … 
ae:=JVM.ExceptionOccurred; 
  if ( ae<>nil ) then 
   begin 
    Writeln(Format('Exception handled in Main.cpp: %d', [longword(ae)])); 
    JVM.ExceptionDescribe; 
    JVM.ExceptionClear; 
   end; 
… …  |   
用方法ExceptionOccurred可以捕获Java抛出的错误,并存入JThrowable类型的变量中。用ExceptionDescribe可以显示出Java的错误信息,而ExceptionClear显然就是清除错误,让它不再被抛出。 
至此,我们已经把从Java代码通过JNI技术访问Delphi本地代码的步骤做了初步的探讨。在jni.pas中也提供了从Delphi中打开Java虚拟机执行Java代码的方法,有兴趣的读者不妨自己研究一下。  
 
  |