楼主: lastwinner

[转载] JAVA应用程序设计开发

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
61#
 楼主| 发表于 2006-6-28 05:03 | 只看该作者
  13.4.3 域变量的访问
  这一节给出例子ObjectDemo的新版本,并结合例子介绍JNI的类型签名,演示如何访问对象的域变量,以及如何使用字符串对象。

  旧版本的Java程序是写在一个文件ObjectDemoMain.java中的,新版本程序与旧版本程序完全相同,可参看例13.11。下面给出ObjectDemo.h的源代码(例13.20)。

  例13.20 ObjectDemo.h

/* DO NOT EDIT THIS FILE - It is machine generated */

#include <jni.h>

/* Header for class ObjectDemo */



#ifndef _Included_ObjectDemo

#define _Included_ObjectDemo

#ifdef cplusplus

extern "C"{

#endif

/*

* Class:ObjectDemo

* Method:display

* SignatureLObjectDemoMainV

*/

JNIEXPORT void JNICALL Java_ObjectDemo_display

(JNIEnv *,jobject,jobject);



#ifdef __cplusplus

}

#endif

#endif

  该文件的第15、16行给出了待实现的方法原型。第13行又有一个签名(“Signature”),这是上一节搁置的问题。“签名”是对基本类型、类甚至方法的类型的表示,是一种关于类型的符号约定。在访问域变量、调用类的方法时,需要知道关于域和方法相关信息,其中就包括对其类型的简明扼要的表示。

  表13.4列出了类型与签名的对应关系。

表13.4 类型签名

类  型 类型签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
<类全名> L<类全名>;
<类型名>[] [<类型签名>
<方法的类型> (<参数类型签名>返回值类型签名

  可根据表13.4分析几个方法的类型。在ObjectDemoMain.java中,方法

  private native void display(ObjectDemoMain obj);

的签名(见ObjectDemo.c)为:

13: *SignatureLObjectDemoMainV

  同表13.4可知,LObjectDemoMain是参数类型签名,V是返值类型签名。查表知“V”代表void,而“LObjectDemoMain;”说明参数是ObjectDemoMain类的对象。

  再看ListDemo.java中声明的方法:

  public native void createList(int[] ints);

它的签名是“([I)V”,易知[I是参数类型签名,[代表数组,I代表int型,故参数为整型数组。返值也是void型。

  当要用到类型签名时,如何得到它呢?按照表13.4直接写当然是可以的,如果怕出错,可以用JDK中的工具java,用

  javap -s -p <类名>

来取得类中域和方法的签名。例如:

C:\bookDemo\ch13>javap -s -p ObjectDemoMain

Compiled from ObjectDemoMain.java

public synchronized class ObjectDemoMain extends java.lang.Object

/* ACC_SUPER bit set */

{

public java.lang.String message;

/* Ljava/lang/String; */

public static void main(java.lang.String[]);

/* ([Ljava/lang/StringV */

public ObjectDemoMain();

/* ()V*/

}

C:\bookDemo\ch13>

  得到了类ObjectDemoMain类的域和方法的类型签名。记住变量message的签名,我们很快就会用到它。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
62#
 楼主| 发表于 2006-6-28 05:04 | 只看该作者
  签名的问题解决了,现在来看实现(例13.21)。

例13.21 ObjectImp.c。

#include <stdio.h>
#include <jni.h>
#include "ObjectDemo.h"
JNIEXPORT void JNICALL Java_ObjectDemo_display
(JNIEnv *env,jobject obj,jobject demo){
jclass clazz=(*env)->GetObjectClass(env,demo);
jfieldID id=(*env)->GetFieldID(env,clazz,"message","Ljava/lang/String;";
jstring js=(*env)->GetObjectField(env,demo,id);
const char *s=(*env)->GetStringUTFChars(env,js,0);
printf("The message is %s\n",s);
(*env)->ReleaseStringUTFChars(env,js,s);
printf("And the length is %d\n",(*env)->getStringLength(env,js));
printf("The length of c string is %d",strlen(s));
}
  这个程序重点说明两个问题:如何访问对象的域;如何操作字符串。
  程序的第6~8行是取对象域的过程。访问一个对象的域,需经过这样一个过程:

  (1)得到域的ID号。JNI为此提供了专门的函数,以便根据该域所在的类、域的名字和类型签名得到ID号。ID号在类被卸载(unload)前一直保持有效。
  为了做到这一点,JNI提供了一些函数:
  ☆ jfieldID GetFieldID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
  得到static型域变量的ID。
  为了得到clazz的值,往往还需要调用一个函数,用来从对象得到对应的类:
  ☆ jclass GetObjectClass(JNIEnv *env,jobject obj);
  例中的第6行用到了它。

  (2)访问该域。对于static域,需要把类作为参数之一,否则用对象作为参数之一。有这样一些函数:
  ☆ <原生方法中的类型>Get<类型*>Field(JNIEnv *env,jobject obj,jfieldID fieldID);
  这也是一族方法,其中的“<类型*>”可以为Object或表13.2中的八种基本类型(前八行)。返回值则是相应的原生方法中的类型(见表13.2)。如:
  jint GetInfField(JNIEnv *env,jobject obj,jfieldID fieldID);
  读一个整形域。
  ☆ void Set<类型*>Field(JNIEnv *env,jobject obj,jfieldID fieldID,<原生方法中的类型> value);
  设置非static型的值。<类型*>、<原生方法中的类型>含义同上。
  ☆<原生方法中的类型> GetStatic<类型*>Field(JNIEnv *env,jclass clazz,jfieldID fieldID);
  读取static型域的值。clazz是域所在的类。
  ☆ Void Set<类型*>StaticField(JINEnv *env,jclass calzz,jfieldID fieldID,<原生方法中的类型>value)
  设置static型域的值。
  读者可以根据上述过程看一下例子的6~8行,验证一下。

  例子的9、11、12行是对字符串的操作。注意,在第8行得到的jstring变量不可直接输出,否则有可能引起虚拟机崩溃。因为Java虚拟机用的字符串是一种特殊的UTF-8字符串,它可以用一个、两个、三个字节来表示信息,而以字节的若干位来标识该信息由几个字节组成。它不同于C中的字符串char*。使用字符串时,必须用JNI提供的函数把它转换成C的字符串格式。

  字符串操作的常用函数如下:
  ☆jstring NewString(JNIEnv *env,const jchar *unicodeChars,jsize len);
  从一个Unicode数组创建一个java.lang.String对象。unicodeChars是指向Unicode数组的指针。
  ☆jsize GetStringLength(JNIEnv *env,jstring string);
  返回字符串长度。
  ☆const jchar *GetStringChars(JNIEnv *env,jstring string,jboolean *isCopy);
  返回一个指向Unicode字符数组的指针,该字符数组由组成字符串的字符组成。像访问一般数组一样,这个指针在执行对应的Release方法(这里是ReleaseStringChars())前保持有效。isCopy决定指针指向的字符数组是否经过拷贝得来。
  ☆ void ReleaseStringchars(JNIEnv *env,jstring string,const jchar *chars);
  通知虚拟机不再需访问该字符串。chars是由GetStringChars()函数得到的。
  ☆ jstring NewStringUTF(JNIEnv *env,const char *bytes,jsize length);
  由一个UTF-8字符组成的数组创建一个java.lang.String对象。bytes是指向UTF-8数组的打针。
  ☆jsize GetStringUTFLength(JNIEnv *env,jstring string);
  返回string的长度(按UTF-8格式计算的字节数)。
  ☆jbyte *GetStringUTFChars(JNIEnv *env,jstring string jboolean *isCopy);
  返回一个指向UTF-8字符数组的指针,该字符数组由组成字符串的字符组成。这个指针在执行对应的Release方法(这里是ReleaseStringUTFChars())前保持有效。isCopy决定指针指向的字符数组是否经过拷贝得来。当你确知串仅由7位的ASCII字符组成时,可以直接把它传递给C的函数,就像例子中做的那样(第9、10行)。
  ☆ void ReleaseStringUTFChars(JNIEnv *env,jstring string,const jbyte *utf);
  通知虚拟机不再需要访问字符串string。utf由GetStringUTFChars()方法得来。
  一般说来,在原生方法中访问字符串都必须经过获得指针、释放指针两步(见示例第9、11行),节不可直接输出未经转换的字符串,以免造成虚拟机崩溃。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
63#
 楼主| 发表于 2006-6-28 05:07 | 只看该作者
  13.4.4 方法调用与异常处理
  本节讲述如何在原生方法中调用Java语言编写的方法,以及如何进行异常处理。

  1.在原生方法中调用Java方法

  在原生方法中调用Java方法,实际上就是访问Java编写的类(对象)的方法。这个工作与访问类、对象的域非常接近。

  (1)得到方法的ID号。用JNI提供的函数
  ☆ jmethodID GetMethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
  可以做到这一点。为了得到方法所在的类的描述(参数clazz),事先还要调用:
  ☆ jclass GetObjectClass(JNIEnv *env,jobject obj);
  例如:
  jclass clazz=(*env)->GetObjectClass(env,obj);
  jmethodID mid=(*env)->GetMethodID(env,clazz,"methodname","(J)V";得到对象Obj中方法
  void methodname(long aLong);
的ID号。
  上面说了对实例方法(非static方法)的处理。对static方法的处理略有不同,应采用函数
  ☆ jmethodID GetStaticMethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig);来得到ID号。
  (2)根据方法返回值类型选用适当函数调用该方法。常用的一族方法是
  ☆ <原生方法中的类型>Call<类型*>Method(JNIEnv *env,jobject obj,jmethodID methodID,...);
<类型*>指“Object”或表13.2中列出的Java基本类型,由被调用方法的返回值决定。如,若被调用方法返回一个对象,则用
  jobject CallObjectMethod(JNIEnv *env,jobject obj,jmethodID methodID,...);“...”是Java方法所需的参数值,只要按原顺序把它们罗列上去即可。如,对于(1)中提到的方法可以这样调用:
  (*env)->CallVoidMethod(env,obj,mid,aLong);
  对于static方法,用的函数不同,应使用:
  ☆<原生方法中的类型>CallStatic<类型*>Method<JNIEnv *env,jclass clazz,jmethodID methodID,...);
来调用。参数的含义同上。

  2.异常处理
  许多JNI方法的调用会引发异常。程序员应当检查每一个可能引起异常的JNI方法执行后的情况,发现有异常及时处理(在前面的例子中我们为了简单没有这样做,但在开发实用程序时这一点不可忽略)。否则,在有未处理异常的情况下调用JNI函数可能引起意想不到的后果。
  下列函数是处理异常时可能用到的:
  ☆jobject ExceptionOccurred(JNIEnv *env);
  捕捉可能发生了的异常;
  ☆ void ExecptionClear(JNIEnv *env);
  清除异常;
  ☆ void ExecptionDescribe(JNIEnv *env);
  输出异常信息。
  例如,我们用这样一段程序:
jthrowable hasExc;
hasExc=(*env)->ExceptionOccured(env);
if(hasExc){
  (*env)->ExceptionDescribe(env);
  (*env)->ExceptionClear(env);
}
可以输出异常信息。
必要时,还可以把异常抛出去。这个工作由
  ☆ jint ThrowNew(JNIEnv *env,jclass clazz,const char *message);
来完成。其中clazz是java.lang.Throwable的子类,而message是字符信息。如:
jclass excClazz;
excClazz=(*env)-FindClass(env,"java/lang/numberFormatException";
  if(excClazz==0)//找不到,则放弃
  return;
  (*env)->ThrowNew(env,excClazz,"thrown from native code";
抛出一个数字格式异常。
  对JNI的使用就介绍到这里。JNI的很多,应用到程序中更会有成千上万种变化。我们这里只能择要介绍一些,其它内容有待读者继续钻研。

  本章小结
  这一章介绍了在程序中应用native方法的一般步骤,并结合实例讲了具体实现的一些细节。
  一般说来,语言间的接口问题都较为复杂的,Java与C的接口也不例外。要自如地运用native方法,不仅要掌握Java相关知识,对C的使用也是一个考验。因此,对于不很有经验的程序员,不要轻易使用它,以免造成严重的后果。

  本章中还概要地解释了Java提供的几个头文件的部分内容。这些解释是有相当局限的,要对Java与C的接口有透彻的了解,还要解读许多内容。这只能让读者在实践中逐步摸索了。

  另外,微软公司的SDK中考虑到垃圾收集和系统的效率,对native方法的实现做了修改。具体哪一种实现更好是个见仁见智的问题。对SDK中提供的接口,在SDK的文档中有较为详细的介绍,可以从网上方便地得到。有兴趣的读者可以自行查阅。

☆☆ 本章结束 ☆☆

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
64#
 楼主| 发表于 2006-6-28 05:08 | 只看该作者
还有几章明天传,西班牙输了,睡觉去

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
65#
 楼主| 发表于 2006-6-29 20:52 | 只看该作者
第十四章 Java虚拟机
  作为一种定位于网络使用的语言,Java具有许多适宜于互连网异构平台环境的特点,包括易移植性、安全性、健壮性和动态性等,其中最突出的是易移植性和安全性。Java的易移植性通过将源程序先编译为虚设的中立平台——Java虚拟机JVM(Java Virtual Machine)的中间指令,再由解释器解释为各具体机器平台可执行的机器码运行来实现。Java的安全性措施包括字节码检验、运行时存储设置、文件获取限制等,贯穿在从源程序编译到最终执行的整个过程中。

  这一章我们将从较深层次出发,首先介绍Java虚拟机JVM的体系结构,然后说明Java的类文件(.class)的组成以及Java程序从编译到执行的完整过程,并说明这一过程中的安全处理措施。

14.1 JVM体系结构
  14.1.1 JVM目的和原理
  Java的设计目的是应用于当前使用日益广泛、影响日益扩大的互连网络。这是一个异构平台环境,可能存在多种不机型,如Intel公司的x86系列,Apple/IBM/Motorola公司的PowerPC机等等。每一种机型都有其特定的中央处理机(CPU)芯片,各芯片的处理过程是不同的。因而通用软件通常需要为每一种类型的机器特别编写版本,以保证正确运行。为了克服这一困难,实现语言的通用性和易移植性,Java的设计者采取的方法是选择一种机器作为编译的目标机,再将编译结果在其它机型上解释执行。但选中的目标机并非上述任何一种实用机型,而是一个假设的处理机平台,称为Java虚拟机JVM(Java Virtual Machine)。

  那么,什么是Java虚拟机呢?我们可以把它定义为:运行编译生成的Java目标代码(即.class类文件)的计算机的实现。JVM实际上是建立在实际处理机基础上的假想计算机。这一假想机可以通过软件仿真实现,也可以通过硬件实现。鉴于目前大多数Java虚拟机还是用软件方法实现的,我们这里介绍的内容也多基于软件实现。

  Java编译器产生的字节码由JVM指令构成,而JVM是虚设的,不接近于任何一种实用机型,这样,一方面编译结果具有平台中立性,不同机型的解释器都可以将字节码文件转换为本机型CPU芯片的适宜机器码来执行。对JVM这一层次而言,操作系统和硬件层都是透明的,用户编写的Java程序,可以在任何平台上运行而无需修改。另一方面,编译生成的字节码接近源生码(native code),可以在任何硬件平台上以较高速度解释运行,实现较高的效率。这样,Java通过虚拟机JVM来试图达到分布式系统的两个相抵触的重要特性:易移植性和高效性之间的平衡和协调。

  接着,我们了解一下JVM的体系结构,包括JVM的指令集、操作码和操作数语法以及取值范围等。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
66#
 楼主| 发表于 2006-6-29 20:52 | 只看该作者
  14.1.2 JVM的结构
  虚拟机JVM由寄存器、栈、废区收集堆、存储区和指令集五部分组成。下面我们将逐一作出介绍。

  1.寄存器(Registers)

  同其他微处理器的寄存器一样,JVM的寄存器用来存放当前系统状态。然而,基于移植性要求,JVM拥有的寄存器数目不能过多。否则,对于任何本身的寄存器个数小于JVM的移植目标机,要用常规存储来模拟高速寄存器,是比较困难的。同时JVM是基于栈(Stack)的,这也使得它拥有的寄存器较少。

  JVM的寄存器包括下面四个:

  (1)PC程序计数寄存器
  (2)optop操作数栈栈顶地址寄存器。
  (3)frame当前执行环境地址寄存器。
  (4)vars局部变量首地址寄存器。

  这些寄存器长度均为32位。其中PC用来记录程序执行步骤,其余optop,frame,vars都存放JVM栈中对应地址,用来快速获取当前执行所需的信息。

  2.栈(Stack)

  JVM是以栈为基本存储机制的处理机。栈的特点是先进后出(FILO)。对每个类的每个方法,JVM都定义一定的栈空间,包含下面三种信息:

  (1)Local Variables局部变量

  这是一个记录各方法局部变量的数组,其初始地址存放在vars寄存器中。每一个数组元素的长度均为32位。若变量长度超过32位,如双精度浮点变量或长整型变量,则占据两个元素的空间64位。

  (2)Execution Enviroment执行环境

  包含代表当前方法的栈的当前状态。存储的信息有:

  *激活的前一个方法。
  *指向局部变量区的指针。
  *指向操作数栈顶和栈底的指针。

  执行环境是执行方法的控制中心,为解释执行和重新编译提供必要的信息。例如,解释器执行JVM的指令iadd,将两整型数相加,执行分为若干步。首先,解释器从寄存器frame中获得当前执行环境。然后,在当前执行环境中指向操作数栈顶的指针,取出要相加的两数。最后还要将所加得的结果回送入栈。

  (3)Operand Stack操作栈

  这是一个以32位为单位长度,用来存储JVM指令的参数的区域。

  3.废区收集堆(Garbage-Collected Heap)

  所有的类被实例化时,所获得的存储空间都是从收集堆中分配的。此外,这个堆还要负责无用空间的回收使用。出于移植性和安全性考虑,Java不赋予程序设计员管理内存空间的权力。因而,在编译用new命令申请新对象存储空间后,由解释器负责跟踪记录这一块内存的使用情况。当使用结束时,回收空间送回堆中。在Sun公司的Java和HotJava环境中,这样的“废区收集”都是作为后台线程运行的,保证了系统运行的高效性。

  4.存储区(Memory Area)

  JVM有两个重要的存储区域,即方法区(method area)和常数池区(constant pool area)。

  方法区存放的是类中定义的各方法的二进制字节码。常数池区存放的则是方法名、类名、域名以及字符串常数。

  5.指令集(Instruction Set)

  指令集是JVM执行的操作码的集合。Java编译器就是将Java源程序转换成JVM的程序:一组JVM指令。

  JVM指令都由一个操作码(opcode)带上零个、一个或两个操作数(operand)组成。操作数长度不尽相同,以8位为基本长度,超过8位时按Big Endian的顺序截断组合,即高位存放在低地址字节中,而低位存放在高地址字节中。操作码长度均为8位。这限制了指令种类最多只能为256(28)种。目前已经被定义使用的操作码有160种,它们包括栈操作、数组操作、算术运算、逻辑运算、数据类型转换、控制流程操作、断点和异常处理等丰富而详尽的内容,这里不再一一赘述。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
67#
 楼主| 发表于 2006-6-29 20:53 | 只看该作者
14.2 深入了解.class文件
  14.2.1 .class文件的结构
  Java的.class文件是用户通过编译命令对源程序进行编译后生成的。由以字节(byte)为单位的字节码(bytecode)组成。每个字节等于8个字位(bit)。由于编译器javac的目标机是虚拟机JVM,因而生成的这此字节码代表的是JVM指令构成的操作码(opcode)和所带的操作数(operand),而非某一特定平台的机器码。

  这里我们要重点说明.class文件的整体结构。一个.class文件通常由15种不同的域组成。这些域的长度有些是固定的,有些则是不变的。其中可变的域都各自有相应的范围字段(size field)指明其长度。而这些范围字段本身的长度固定为2字节。

  下面将给出这15种域的域名和存储内容简介。

  *magic 存储一固定值0xCAFFBABE。
  *version 存储生成该.class文件的编译器的版本号。
  *constant_pool_count 指明域constant_pool中数组元素个数,即域constant_pool长度。
  *constant_pool[constant_pool_count-1] 存储含有constant_pool_count-1个元素的数组,包括类名、域名及各常量信息。
  *access_flags 长度为2字节,指明各域、类和方法的16种不同存取限制。
  *this_class 长度为2字节,存储一个索引,根据该索引值可在constant_pool域找到相应的有关当前类的信息。
  *super_class 与域this_class类似,存储当前类的父类信息。
  *interfaces_count 指明域interfaces长度。
  *interfaces[interfaces_count] 存储有interfaces_count个元素的数组,每一元素均为一索引值,在域constant_pool的相应索引值位置存储当前类完成的接口的有关信息。
  *fields_count 指明域fields的长度。
  *filds[fileds_count] 存储一含有fields_count个元素的数组,记录有关类的各域的完整信息。其中每个元素包含一个2字节的access_flags域,两个2字节的constant_pool的索引,一个2字节的attributes_count域以及一个attributes数组。
  *attributes_count 指明域以及attributes的长度。
  *attributes[attributes_count] 存储一个含有attributes_count个元素的属性结构数组。属性可能的类型包括源文件(SourceFile)、常量值(ConstantValue)、代码(Code)和异常(Exceptions)等等。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
68#
 楼主| 发表于 2006-6-29 20:53 | 只看该作者
  14.2.2 执行过程与安全措施
  Java源程序经编译生成JVM字节码程序后,执行的工作是由解释器通过下面三个步骤完成的:类的装入、正确性检验和代码执行。

  1、类的装入

  类的装入工作是由类装入器(Class Loader)完成的。它可以实现从本地机或远地装入当前应用的类和所继承的类。这是执行Java字节码的第一步工作。由于类装入器的支持,Java程序可以自动地装入和运行所需的类,包括从网络上不同结点处装入运行。这一特点所带来的安全性问题是让网络管理员大为担心的,然而接下来我们将看到Java的设计者针对这一特点所设置的详尽的安全措施。

  首先,类装入器将从其它结点装入的类存放在它们特定的存储空间,与本地机定义的类的存储空间分隔开,互不干扰。这样既保证了本地类执行的效率,又保护它们不受外部引入的破坏与干扰。

  其次,Java还对文件获取作一定的限制。从外部装入类,除了被客户机或用户允许的特定情形外,是不允许获取本地机的文件系统的,这也保证了系统的安全性和保密性。

  2.正确性检验

  解释器要进行的第二步工作是对字节码进行正确性检验。这一步骤的目的是防止在程序编译之后,运行之前对字节码进行的有意或无意的改动带来的问题。在这一步中,所有的字节码依次通过一个字节码检验器(bytecode verifier),由检验器逐行检查。通常检验器可以查出的错误包括:

  *伪指针。
  *违反存取限制。
  *对象不匹配。
  *操作数栈的上溢和下溢。
  *参数错误。
  *非法数据转换。

  字节码的正确性检验既能防止可能造成系统崩溃的错误过程的运行,又能保证下一步骤的代码运行的连续性,避免中途停顿以检查和处理异常。

  3.代码执行

  解释器的最后一项工作是代码的执行。在此之前,先要完成类的存储设置。Java在执行时才进行存储设置的特点,使网络“黑客”们无法预先得知类在硬件层次上的分配结构,也就无法藉此探知整个系统结构和获取路径,从而有效地保护整个系统不受外来侵入。

  字节码的解释执行实际上是解释器将字节码转化成客户机系统平台可进行的操作,然后由客户机平台自行运行的。这样就同时保证了字节码的可移植性和高效性。无论客户机采用何种操作系统,只要配有Java解释器,就能实现字节码的移植执行。而将字节码转换成各平台相应执行的机器码,又保证了执行的效率。

本章小结
  这一章通过对Java虚拟机JVM及Java程序实际执行的全过程的介绍。阐明了Java语言突出的易移植性和安全性特点,使读者对Java程序的编译和解释执行了有更深层次的理解。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
69#
 楼主| 发表于 2006-6-29 20:53 | 只看该作者
第十五章 Java豆(Java Beans)
  本章介绍“Java豆”的概念、相关类库和开发的基本方法。

  “Java豆”是Java技术的一个发展。Java语言的设计者们于1996年底推出了Java豆应用程序接口的正式说明书。引入Java豆的目的是为Java提供一个新的软件组件(Component)模型,以便使用Java开发的软件可以很方便地被重复使用,或组装起来以适应新的需要。简单地说,意图是让开发者开发出来的程序(豆)相对独立,具有统一的接口,如同积木一般可以装配。尽管这一概念尚不完善,但已引起许多人士的注意。“Java豆”的发展将为软件复用提供一种新的解决方案。

  本章的内容与前面各章相对独立。不仅如此,在系统软件方面也有较高的要求。实验Java豆要求JDK版本在1.1版本以上,并至少拥有BDK1.0版本。BDK(Beans Development Kit)是一个与JDK配合使用的工具包,它能演示“豆”被操作和使用的过程,并支持简单的Java豆开发。BDK可以从“ftp://splash.javasoft.com/pub”下载。

  学习完本章以后,读者应对“Java豆”有较全面的了解,熟悉BDK的使用和JDK1.1版本中与Java豆相关的改进,并能开发简单的Java豆。

  15.1 什么是Java豆
  这一节我们先对Java豆建立一个初步的感性认识,然后阐述其概念。

  15.1.1 BDK的使用——认识Java豆
  我们先把BDK运行起来。假设你拥有JDK1.1.1版本和BDK1.0版本,分别安装在c:\JDK1.1.1和c:\BDK目录下。运行你的BDK的操作步骤如下:

  (1)打开一个命令提示窗口,进入BDK安装目录的beanbox子目录。

  (2)设置路径为JDK1.1.1版本的bin目录,如\JDK1.1.1\bin。

  (3)在命令提示符下键入“run”并回车。

  “run”批命令陆续被执行,最后,屏幕上出现三个窗口(见图15.1),标题栏分别为“ToolBox”,“BeanBox”和“PropertySheet”。这三个窗口的出现可能要花费稍长的时间。只要不出现异常(Exception),请耐心等待。

  三个窗口打开后,BDK已进入了工作状态。ToolBox窗口中有16个项,代表了BDK中附带的16个Java豆示例。可以看到有的豆带有图标。PropertySheet窗口用于编辑豆的属性(主要是外观属性)。中间的大窗口BeanBox是用来装配和编辑豆的,它有两个菜单项,完成对豆的存取、编辑操作。

  下面我们用BDK附带的示例Java豆做几个小实验,以验证BDK的功能。

  实验1 关联属性

  关联属性是豆属性的一种,基本的含义是将两个豆的若干同一类型的属性关联起来,使一个豆的属性发生改变时,另一个相应属性也随之改变。15.3.2将予以详细介绍。这个实验将演示BDK的基本操作,并把示例豆“TransitionalBean”和“JellyBean”的属性“color”关联起来。

  点击BeanBox的Edit菜单项,看到五个项。这是对于当前对象(当前是Beanbox)可以施行的操作,往Beanbox中放入一个“豆”,菜单项可能发生改变。按下面步骤操作:

  (1)以鼠标点击ToolBox中的“TransitionalBean”。光标变为十字型。

  (2)在BeanBox的中央偏上处点击一下,出现一个用桔黄色填充的圆角矩形。它周围带有短粗斜线组成的方框,表示它是当前被选中的对象。

  (3)观察PropertySheet窗口。其中的内容发生了变化,它包含着TransitionBean可以直接修改的属性。看看Edit菜单项的下拉菜单,无变化。

  (4)用鼠标点击ToolBox中的“JellyBean”。

  (5)在BeanBox中,TransitionalBean的下方点击一下,出现与(2)中相同的图案。

  (6)观察PropertySheet窗口,与(3)中相比,多了一些属性,可见两个豆外观虽相似,功能却不同。

  (7)点击Edit菜单项,发现多了一项“Bind Property...”点击之,出现属性名对话框(“PropertyNameDialog”)。点中“color”,并点“OK”键。

  (8)把光标在BeanBox中移动,可见它牵引着一条从JellyBean发出的射线。点击TransitionalBean,又出现属性名对话框。点中“color”,并点击“OK”键。

  (9)点击JellyBean,使它被短粗斜线组成的矩形框包围,即被选中。

  (10)在PropertySheet窗口中的“color”后的桔黄色矩形上点击,出现颜色编辑器对话框(“sun.beans.editors.ColorEditor”)。其右部的下拉列表框中显示“orange”,将其值改为“red”。可以看到,两个圆角矩形都变成了红色。

  (11)再改变几次颜色,可以发现,改变JellyBean的颜色将导致同时改变TransitionalBean的颜色,反之亦然。

  上面的例子演示了两个豆的“关联属性”是如何被BDK关联的。应注意,BDK只是一个演示性的工具包,仅仅支持初步的开发,其目的与其说是作为开发工具不如说是作为正式的(将来的)开发集成环境做功能上的示意。因此,它的界面较为粗糙,运行也不稳定。但目前BDK仍是帮助我们了解Java豆的必不可少的工具。

  实验2 几个豆的协同工作。

  在这个实验中将演示如何为几个豆建立相互之间的事件处理关系,使得某个豆可以引起别的豆的反应。这实际上就是把若干豆装配到一起以协同完成一项工作的过程。

  用“File”菜单的“Clear”选项清除整个BeanBox窗口的内容,进入第二个实验。

  (1)点击Toolbox中的“Juggler”项。

  (2)在BeanBox的中央点击一下,之所以在中央是因为该豆的图像面积较大。如果位置不合适,造成图像不能完全显示也不要紧,可以像移动一个窗口一样,用鼠标点在短粗斜线组成的边框上拖曳以移动图像。可以看到BeanBox中间出现Juggler的动画。

  (3)点击“ToolBox”中的“ExplicitButton”,放在BeanBox中的空白处。相应位置将出现一个按钮,上面写着“Press”。在选中该按钮的情况下,在PropertySheet中找到“label”栏,将其中的“press”改成“Start”。

  (4)点击ToolBox中的“OurButton”,置于BeanBox中。类似(3),将按钮上的文字改为“Stop”。

  (5)分别选中“Start”“Stop”按钮,比较它们的PropertySheet窗口内窗口有什么不同,Edit子菜单的内容有何不同,并比较Edit子菜单的Events项引出的次级子菜单。

  (6)选中“Start”按钮,点击Edit菜单项的Events子菜单中的button push项,直到出现“actionPerformed”项,点击之。可以看到自Start按钮引出一条射线。点击Juggler豆,出现事件目标对话框(“EventTargetDialog”)。

  (7)选中“startJuggling”项,点击“OK”,等待对话框自动消失。

  (8)此时BeanBox中的Juggler豆处于被选中状态。用鼠标点击“Stop”按钮,使它被选中。在Edit菜单中,逐级点击Events|action|actionPerformed,可见从“Stop”按钮引出一条射线。

  (9)点击Juggler豆,再次出现事件目标对话框,选择“stopJuggling”项并按“OK”。等待对话框自动消失。

  (10)按动Stop按钮,Juggler豆的动画就静止了,按Start键则动画重新开始。

  (11)选中Juggler豆,将PropertySheet窗口上的AnimationRate(动画速率)的值由125改为500。可以看到,按动Start后,动画速率比原来明显变慢。

  在实验2中,我们把两个按钮与Juggler豆关联起来。我们用到的Star键是ExplicitButton的一个实例,而Stop键是OurButton类的实例,ExplicitButton系从OurButton派生而来。可以看到,Juggler豆对两个按钮的事件作出了响应。

  以上两个实验是为了给读者一个感性认识。具体的原理(如事件传递、属性窗口的不同、编辑菜单项为何不同)将在本章的后续各节分别介绍。读者还可看BDK的有关文档,自己组装编辑Java豆。

  当一个豆被选中时,它可以被移动,有的豆还可以改变大小。选中一个豆的方法就是点击它。但有时可以发现点击一个豆似乎不起作用,这时你可以点击它的边缘,一般就可以达到目的了。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
70#
 楼主| 发表于 2006-6-29 20:54 | 只看该作者
  15.1.2 什么是豆
  Java豆应用程序接口说明中,为“豆”给出了这样的定义:“Java豆是一种可重用的软件组件,它可以在创建工具中被可视化地操纵”。

  如果有过使用Visual Basic,VisualAge或Delphi的经验,读者将不难理解这一定义。不过,即使没有接触过类似的东西也不要紧,我们可以依据15.1.1中的实验对这一定义做一些说明。

  上一节的实验2中,我们使用了三个豆,做了一个小小的动画演示。现在我们倒过来看这个问题,假如要演示动画,我们应做什么?显然需要有显示区域和命令输入。于是我们选用了三个豆。以ExplicitButton为例,它被开发并非为我们这个演示,也不仅仅用于这个例子中;它可以用在一切适宜的需要一个按钮的地方。这个按钮就是一个“可重用的软件组件”,即软件的一个相对独立的组成部分,并可以被重复使用。

  许多开发环境允许程序员进行可视化操作,往往它们提供一个叫做“form”或“window”或其它名字的“白板”,开发人员可以把自己所需要的部件(如滚动条、按钮、下拉列表框等)用鼠标拖放在上面,组成一个整体。虽然这些开发环境对于开发带有图形用户界面的应用尤为适合,但并不局限于可视的应用。这些开发环境就是所谓的“创建工具”。BDK虽然比较简单和粗糙,但仍然称得上是一个创建工具。当然,为了开发实用的“豆”,我们还需要更多的功能,如文本编辑、图形编辑等等。

  至于可视化操纵,经过15.1.1中用鼠标操纵了若干个豆后,它的意义对读者来说是很明显的。Java豆就是这样的一种组件。应该说,可重用的软件这一思想久已有之,虽然形式不同,基本思想是一样的。它们往往要求统一的标准接口,并提供相应的机制(参见15.4“自省”)让创建工具了解它的性质、行为,从而使开发人员能够运用它们、组装它们。Java豆也在寻求与已有的软件组件(如微软的Active X)相联接的方法,并已取得一定成效。

  Java豆具有如下的典型特征,正是这些特征保证了它的功能:

  (1)“自省”功能(Introspection),即提供相应的机制,使创建工具能够分析它的行为和性质。

  (2)可以定制(Customize)。即某些属性(properties)可以通过创建工具来修改。如15.1.1中,我们用BDK的PropertySheet窗口修改了ExplicitBtton的标签。

  (3)可以被组合。这是通过事件机制来完成的(详见15.2)。

  (4)可以存储、装入。这使得对豆编辑和联接的结果可以被录入保存,并在适当的时候取出来应用。BDK的File菜单中有存储和装载豆的菜单项,读者不妨一试。

  Java豆的三大要素是属性(Properties)、允许其它组件调用的方法集和能够激发的事件集。

  属性值可以通过调用相应的方法来修改或获取。如用getLabel()获取标签值,setLabel()设置标签值等。

  public方法一般来说都可以被其他组件调用,但豆可以限定只有部分public方法可以被调用。

  事件是组件之间联系的手段,JDK1.1版本和BDK中引入了一种有别于过去的事件处理的模式,即代理(Delegation)模式。在这种模式中,存在事件源与事件监听者。二者通过“登记”联系起来,一旦事件源处发生了相应的事件,对应的监听者的方法就被调用来处理它。

  以上对Java豆做了概要的介绍。后面几节中,我们先分别介绍Java豆应用程序接口的各方面的规则,然后结合实例介绍Java豆的开发。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表