Java类加载器初探

来源:互联网 发布:淘宝上怎么样开店 编辑:程序博客网 时间:2024/05/11 12:34

 

简介类加载器(class loader)是Java中的一个很重要的概念。类加载器负责加载Java类的字节代码到Java虚拟机中。本文首先详细介绍了Java类加载器的基本概念,Java虚拟机自带的几种类加载器,以及Java开发中CLASSPATH设置中的一些误区。

类加载器是Java语言的一个创新,也是Java语言流行的重要原因之一。它使得Java类可以被
动态加载到Java虚拟机中并执行。类加载器从JDK1.0就出现了,最初是为了满足Java Applet的需要而开发出来的。Java Applet需要从远程下载Java类文件到浏览器中并执行。现在类加载器在WEB容器和OSGI中得到了广泛的使用。一般来说,Java应用的开发人员不需要直接同类加载器进行交互。Java虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机会又不是很了解的话,就很容易花大量的时间去调试ClassNotFoundException和NoClassDefFoundError等异常。
类加载器的概念:
顾名思义,类加载器(class loader)用来加载Java类到虚拟机中。一般来说,Java虚拟机使用Java类的方式如下:Java源文件(.java)在经过Java编译器编译之后就被转换成Java字节代码(.class文件)。类加载器负责读取Java字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个Java类。通过实例的newInstance()方法就可以创建出该类的一个对象。实际的情况可能更复杂,比如Java字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
中间可能还经过链接,验证等环节。

Java类加载器的机制
Java类加载过程采用父亲委托机制,这种机制能更好地保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器外,其余的类加载器都只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则则由加载器loader1本身加载Sample类。
Java虚拟机自带了哪些类加载器?
根(bootstrap)类加载器:也称为引导类加载器。该加载器没有父加载器,它负责加载虚拟机的核心类型,比如java.lang.*等。在后面的测试代码中看到java.lang.Object就是由根类加载器加载的。更准确地说,根类型器(bootstrap)从系统属性sun.boot.class.path所指定的目录中加载类库。根类加载器的实现依赖于底层操作系统,属性虚拟机的实现的一部分,本身并不是用java写的,可能是c/c++,所以它并没有继承java.lang.ClassLoader类。
扩展(Exstension)类加载器:它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库,如果把用户创建的jar文件放到这个目录下,它也会自动由扩展器加载。扩展类加载器是纯Java类。是java.lang.ClassLoader类的子类。后面会提到ClassPath的设置误区。
系统(System)类加载器:也称为应用类加载器,它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器。如后面提到的Person类。系统类加载器是纯Java类,是java.lang.ClassLoader的子类。
测试与验证
测试
test1:测试jvm自带的几个类加载器
package com.jfans;
public class ClassLoaderTest1 {
public static void main(String[] args) {
     System.out.println(String.class.getClassLoader());//根(引导)类加载器
     System.out.println(Object.class.getClassLoader());//根(引导)类加载器
     System.out.println(Person.class.getClassLoader());//系统类加载器,自定义的Person类
     
     System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
     ClassLoader loader = ClassLoader.getSystemClassLoader();//返回系统类加载器(在虚拟机启动时就会产生)
 System.out.println(loader);
}
}
结果:
null
null
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$ExtClassLoader@923e30
sun.misc.Launcher$AppClassLoader@82ba41
结果说明:输出为null即表示是根类加载器
test2:返回几个与class loader相关的几个路径
package com.jfans;
public class ClassLoaderTest3 {
public static void main(String[] args) {
String sunBootClassPath = System.getProperty("sun.boot.class.path");
System.out.println("sunBootClassPath: " + sunBootClassPath);
    String javaExtDirs = System.getProperty("java.ext.dirs");
    System.out.println("extDir: " + javaExtDirs);
    String javaClassPath = System.getProperty("java.class.path");
    System.out.println("javaClassPath: " + javaClassPath);
    System.out.println(java.lang.ClassLoader.class.getClassLoader());
}
}
结果:
sunBootClassPath: E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/rt.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/i18n.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/sunrsasign.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/jsse.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/jce.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/charsets.jar;E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/classes
extDir: E:/DevelopmentTools/JDK5.0/Java/jdk1.5.0_05/jre/lib/ext
javaClassPath: D:/workspace_job/ClassLoader/bin
说明:测试平台为windows xp,jdk5.0,jdk安装目录为E:/DevelopmentTools/JDK5.0/Java/


说明:rt.jar是由根类加载器加载的,rt为runtime的缩写,里面是核心java类
Classpath设置的误区:
经常看到一些人和一些Java教材上写设置classpath内容为:.;%JAVA_HOME%/jre/lib/rt.jar;后面可能还接有其它jar。通过上面的理论部分和测试结果可知,rt.jar实际上已经由根类加载器加载了。
所以classpaht中根本没有必要设置%JAVA_HOME%/jre/lib/rt.jar;只需要设置为:.(当前目录)即可。