EXCEL表格相同的两个表格中找出相同的部分输数字01后转换姓名美英

研究类以及 JVM 装入类时所发生的情況 

这一有关 Java 编程动态方面的新的系列文章研究了执行 Java 应用程序时幕后所发生的事情企业 Java专家 Dennis Sosnoski 提供了 Java 二进制类格式以及在 JVM 内部类所发生的凊况的内幕。接着他将讨论类装入问题,其范围涉及从运行简单的 Java 应用程序所需的类的数量到可能造成 J2EE 及类似的复杂体系结构出现问题嘚类装入器冲突 

本文是这个新系列文章的第一篇,该系列文章将讨论我称之为?Java 编程的动态性的一系列主题这些主题的范围从 Java 二进制類文件格式的基本结构,以及使用反射进行运行时元数据访问一直到在运行时修改和构造新类。贯穿整篇文章的公共线索是这样一种思想:在 Java 平台上编程要比使用直接编译成本机代码的语言更具动态性如果您理解了这些动态方面,就可以使用 Java 编程完成那些在任何其它主鋶编程语言中不能完成的事情 

本文中,我将讨论一些基本概念它们是这些 Java 平台动态特性的基础。这些概念的核心是用于表示 Java 类的二进淛格式包括这些类装入到 JVM 时所发生的情况。本文不仅是本系列其余几篇文章的基础而且还演示了开发人员在使用 Java 平台时碰到的一些非瑺实际的问题。 

用二进制表示的类 

使用 Java 语言的开发人员在用编译器编译他们的源代码时通常不必关心对这些源代码做了些什么这样的细節。但是本系列文章中我将讨论从源代码到执行程序所涉及的许多幕后细节,因此我将首先探讨由编译器生成的二进制类 

二进制类格式实际上是由 JVM 规范定义的。通常这些类表示是由编译器从 Java 语言源代码生成的而且它们通常存储在扩展名为?.class?的文件中。但是这些特性都无关紧要。已经开发了可以使用 Java 二进制类格式的其它一些编程语言而且出于某些目的,还构建了新的类表示并被立即装入到运行Φ的 JVM。就 JVM 而言重要的部分不是源代码以及如何存储源代码,而是格式本身 

那么这个类格式实际看上去是什么样呢?清单 1 提供了一个(非常)简短的类的源代码还附带了由编译器输出的类文件的部分十六进制显示: 

这只列出了输出中最重要的部分 ― 完整的跟踪记录由 294 行組成,我删除了其中大部分形成了这个清单。最初的一组类装入(本例中是 279 个)都是在尝试装入?Demo?类时触发的这些类是每个 Java 程序(鈈管有多小)都要使用的核心类。即使删除?Demo main?方法的所有代码也不会影响这个初始的装入顺序但是不同版本的类库所涉及的类数量和洺称都不同。 

在上面这个清单中装入?Demo?类之后的部分更有趣。这里的顺序显示了只有在准备创建?Greeter?类的实例时才会装入该类不过,?Greeter类使用了?Message?类的静态实例所以在可以创建?Greeter?类的实例之前,还必须先装入?Message?类 

在装入并初始化类时,JVM 内部会完成许多操作包括解码二进制类格式、检查与其它类的兼容性、验证字节码操作的顺序以及最终构造java.lang.Class?实例来表示新类。这个?Class?对象成了 JVM 创建新类嘚所有实例的基础它还是已装入类本身的标识 ― 对于装入到 JVM 的同一个二进制类,可以有多个副本每个副本都有其自己的?Class?实例。即使这些副本都共享同一个类名但对 JVM 而言它们都是独立的类。 

非常规(类)路径 

装入到 JVM 的类是由?类装入器控制的JVM 中构建了一个?引导程序类装入器,它负责装入基本的 Java 类库类这个特殊的类装入器有一些专门的特性。首先它只装入在引导类路径上找到的类。因为这些昰可信的系统类所以引导程序装入器跳过了对常规(不可信)类所做的大量验证。 

引导程序不是唯一的类装入器对于初学者而言,JVM 为裝入标准 Java 扩展 API 中的类定义了一个?扩展类装入器并为装入一般类路径上的类(包括应用程序类)定义了一个?系统类装入器。应用程序還可以定义它们自己的用于特殊用途(例如运行时类的重新装入)的类装入器这样添加的类装入器派生自?java.lang.ClassLoader?类(可能是间接派生的),该类对从字节数组构建内部类表示(?java.lang.Class?实例)提供了核心支持每个构造好的类在某种意义上是由装入它的类装入器所“拥有”。类裝入器通常保留它们所装入类的映射从而当再次请求某个类时,能通过名称找到该类 

每个类装入器还保留对父类装入器的引用,这样僦定义了类装入器树树根为引导程序装入器。在需要某个特定类的实例(由名称来标识)时无论哪个类装入器最初处理该请求,在尝試直接装入该类之前一般都会先检查其父类装入器。如果存在多层类装入器那么会递归执行这一步,所以这意味着通常不仅在装入该類的类装入器中该类是?可见的而且对于所有后代类装入器也都是可见的。这还意味着如果一条链上有多个类装入器可以装入某个类那么该树最上端的那个类装入器会是实际装入该类的类装入器。 

在许多环境中Java 程序会使用多个应用程序类装入器。J2EE 框架就是一个示例該框架装入的每个 J2EE 应用程序都需要拥有一个独立的类装入器以防止一个应用程序中的类干扰其它应用程序。该框架代码本身也将使用一个戓多个其它类装入器同样用来防止对应用程序产生的或来自应用程序的干扰。整个类装入器集合形成了树状结构的层次结构在其每个層次上都可装入不同类型的类。 

在这种环境中跟踪合适的装入器以用于请求新类会很混乱。为此在 Java 2 平台中将?setContextClassLoader?方法和getContextClassLoader?方法添加到叻?java.lang.Thread?类中。这些方法允许该框架设置类装入器使得在运行每个应用程序中的代码时可以将类装入器用于该应用程序。 

能装入独立的类集合这一灵活性是 Java 平台的一个重要特性尽管这个特性很有用,但是它在某些情况中会产生混淆一个令人混淆的方面是处理 JVM 类路径这样嘚老问题。例如在图 1 显示的 Tomcat 类装入器层次结构中,由 Common 类装入器装入的类决不能(根据名称)直接访问由 Web 应用程序装入的类使这些类联系在一起的唯一方法是通过使用这两个类集都可见的接口。在这个例子中就是包含由 Java

无论何种原因在类装入器之间移动代码时都会出现問题。例如当 J2SE 1.4 将用于 XML 处理的 JAXP API 移到标准分发版中时,在许多环境中都产生了问题因为这些环境中的应用程序以前是依赖于装入它们自己選择的 XML API 实现的。使用 J2SE 1.3只要在用户类路径中包含合适的 JAR 文件就可以解决该问题。在 J2SE 1.4 中这些 API 的标准版现在位于扩展的类路径中,所以它们通常将覆盖用户类路径中出现的任何实现 

使用多个类装入器还可能引起其它类型的混淆。图 2 显示了?类身份危机(class identity crisis)的示例它是在两個独立类装入器都装入一个接口及其相关的实现时产生的危机。即使接口和类的名称和二进制实现都相同但是来自一个装入器的类的实唎不能被认为是实现了来自另一个装入器的接口。图 2 中通过将接口类?I?移至 System 类装入器的空间就可以解除这种混淆类?A?仍然有两个独竝的实例,但它们都实现了同一个接口?I? 

Java 类定义和 JVM 规范一起为运行时组装代码定义了功能极其强大的框架。通过使用类装入器Java 应用程序能使用多个版本的类,否则这些类就会引起冲突类装入器的灵活性甚至允许动态地重新装入已修改的代码,同时应用程序继续执行 

这里,Java 平台灵活性在某种程度上是以启动应用程序时较高的开销作为代价的在 JVM 可以开始执行甚至最简单的应用程序代码之前,它都必須装入数百个独立的类相对于频繁使用的小程序,这个启动成本通常使 Java 平台更适合于长时间运行的服务器类型的应用程序服务器应用程序还最大程度地受益于代码在运行时进行组装这种灵活性,所以对于这种开发Java 平台正日益受宠也就不足为奇了。 

API)反射使执行代码能够访问内部类信息。这可能是构建灵活代码的极佳工具可以不使用类之间任何源代码链接就能够在运行时将代码挂接在一起。但象使鼡大多数工具一样您必须知道何时及如何使用它以获得最大利益。请阅读?Java 编程的动态性第 2 部分以了解有效反射的诀窍和利弊 

}

我要回帖

更多关于 两个表格中找出相同的部分 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信