Java程序类加载完全揭密
来源:网络 更新时间:2014-12-3
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
类加载是Java语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题,但所有的编程人员都应该了解其工作机制,明白如何做才能让其满足我们的需要。这能有效节省我们的编码时间,从不断调试ClassNotFoundException,ClassCastException的工作中解脱出来。
这篇文章从基础讲起,比如代码与数据的不同之处是什么,他们是如何构成一个实例或对象的。然后深入探讨java虚拟机(JVM)是如何利用类加载器读取代码,以及java中类加载器的主要类型。接着用一个类加载的基本算法看一下类加载器如何加载一个内部类。本文的下一节演示一段代码来说明扩展和开发属于自己的类加载器的必要性。紧接着解释如何使用定制的类加载器来完成一个一般意义上的任务,使其可以加载任意远端客户的代码,在JVM中定义,实例化并执行它。本文包括了J2EE关于类加载的规范——事实上这已经成为了J2EE的标准之一。
类与数据
一个类代表要执行的代码,而数据则表示其相关状态。状态时常改变,而代码则不会。当我们将一个特定的状态与一个类相对应起来,也就意味着将一个类事例化。尽管相同的类对应的实例其状态千差万别,但其本质都对应着同一段代码。在JAVA中,一个类通常有着一个.class文件,但也有例外。在JAVA的运行时环境中(Javaruntime),每一个类都有一个以第一类(first-class)的Java对象所表现出现的代码,其是java.lang.Class的实例。我们编译一个JAVA文件,编译器都会嵌入一个public,static,final修饰的类型为java.lang.Class,名称为class的域变量在其字节码文件中。因为使用了public修饰,我们可以采用如下的形式对其访问:
java.lang.Classklass=Myclass.class;
一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。这里存在一个问题就是什么是“同一个类”?正如一个对象有一个具体的状态,即标识,一个对象始终和其代码(类)相关联。同理,载入JVM的类也有一个具体的标识,我们接下来看。
在Java中,一个类用其完全匹配类名(fullyqualifiedclassname)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器KlassLoader的一个实例kl1加载,Cl的实例,即C1.class在JVM中表示为(Cl,Pg,kl1)。这意味着两个类加载器的实例(Cl,Pg,kl1)和(Cl,Pg,kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的。那么在JVM中到底有多少种类加载器的实例?下一节我们揭示答案。
类加载器
在JVM中,每一个类都被java.lang.ClassLoader的一些实例来加载.类ClassLoader是在包中java.lang里,开发者可以自由地继承它并添加自己的功能来加载类。
无论何时我们键入javaMyMainClass来开始运行一个新的JVM,“引导类加载器(bootstrapclassloader)”负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。运行时的类在JRE\lib\rt.jar包文件中。因为这属于系统底层执行动作,我们无法在JAVA文档中找到引导类加载器的工作细节。基于同样的原因,引导类加载器的行为在各JVM之间也是大相径庭。
同理,如果我们按照如下方式:
log(java.lang.String.class.getClassLoader());
来获取java的核心运行时类的加载器,就会得到null。
接下来介绍java的扩展类加载器。扩展库提供比java运行代码更多的特性,我们可以把扩展库保存在由java.ext.dirs属性提供的路径中。
(编辑注:java.ext.dirs属性指的是系统属性下的一个key,所有的系统属性可以通过System.getProperties()方法获得。在编者的系统中,java.ext.dirs的value是”C:\ProgramFiles\Java\JDK1.5.0_04\jre\lib\ext”。下面将要谈到的如java.class.path也同属系统属性的一个key。)