读书人

种和类装入

发布时间: 2012-09-05 15:19:34 作者: rapoo

类和类装入

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

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

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

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

无论何种原因在类装入器之间移动代码时都会出现问题。例如,当 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


图 2. 类身份危机
?种和类装入

结束语

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

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

在本系列文章的第 2 部分中,我将介绍使用 Java 平台动态基础的另一个方面:反射 API(Reflection API)。反射使执行代码能够访问内部类信息。这可能是构建灵活代码的极佳工具,可以不使用类之间任何源代码链接就能够在运行时将代码挂接在一起。但象使用大多数工具一样,您必须知道何时及如何使用它以获得最大利益。请阅读 Java 编程的动态性第 2 部分以了解有效反射的诀窍和利弊。

读书人网 >编程

热点推荐