类加载概述

上一篇中,我们了解了class文件的基本内容,从那里,我们知道class文件是包括一些自描述信息的一个文件,这个文件将会在jvm运行期间 被虚拟机加载。当class文件被加载到jvm中后,最终会生成Class对象,在有了Class对象后,虚拟机便可以实例化它并在适当的时候 调用。加载的过程可以发生在虚拟机启动时,也可以在虚拟机运行期间动态加载外部的类或者从网络传输来的类。总的来说,类的 加载机制就是class文件被加载到jvm并生成Class对象的过程。

类加载的过程

主要分为: * 装载 * 链接 * 初始化

###装载

类装载的过程是指从class文件中找到二进制字节码并加载到jvm中,在这过程中,jvm通过类的全限定名以及类加载器完成类的加载, 当类被加载后,通过全限定名和类加载器实例id来标识。对于类名,也有一套规则如下: 对于接口或者非数组型的类,其名称就是类名,这种类型的类由所在的ClassLoader负责加载。而对于数组类型,它的名称为

“[”    

加上基本类型或者是

“[”

加上引用类型类名。这一点,可以轻松的通过程序来验证:

public class LoadClassDemo {
    public static void main(String[] args) {
    int[] arr = new int[12];      
    byte[] byteArr = new byte[20];
    Object obj = new Object();
    LoadClassDemo[] classArr = new LoadClassDemo[33];
    System.out.println(arr.getClass());  //class \[I
    System.out.println(byteArr.getClass());//class \[[B
    System.out.println(obj.getClass()); //class java.lang.Object
    System.out.println(classArr.getClass());//class \[Lcom.jvmdemo.LoadClassDemo
    System.out.println(LoadClassDemo.class);//class com.jvmdemo.LoadClassDemo
    }
}

这里只取一部分,如果你有兴趣可以去探索更多的名称,不过不管怎么样,都是按照前文提到的命名规则。我们从测试中也看到 对于类的名称或者数组中引用类型类名,其类名都是全限定名的(带有包名或者说是命名空间的)。另外,在数组型类中,元素类型 由其所在的classLoader负责加载,而数组类则由jvm直接创建。

###链接

经过装载过程,接下来的过程就是链接了,链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量,解析类中调用的 接口、类。 首先进行检验,字节码按照jvm规范进行校验,如果不符合则抛出VerifyError。在进行检验的过程中,如果该类引用了其它类,则也会进行 加载,如果加载过程失败,则会抛出NoClassDefFoundError。 完成校验步骤后,随后就jvm就会初始化类中的静态变量,并将它的值赋值为默认值。 最后对类中的属性,方法验证。如果验证失败,则会抛出NoSuchMethodError,NoSuchFieldError等信息。

###初始化

完成链接过程后,就开始初始化类中的静态代码块,构造器和静态属性。

几种类加载器

  • Bootstrap ClassLoader
  • Extension ClassLoader
  • System ClassLoader
  • User-Defined ClassLoader

其中,Bootstrap 类加载器是有JDK采用c++实现的,因此无法在java代码中获取该对象。JDK启动的时候,初始化该加载器。它主要完成加载 JAVA_HOME中jre/lib/rt.jar中class文件。而Extension ClassLoader主要用来加载扩展功能的jar包,所以叫做扩展类加载器。它在Sun jdk中的ClassLoader 对应的名称是ExtClassLoader。接着来看System ClassLoader,这个加载器用了加载启动参数中指定的Classpath中的jar包和目录。它在Sun jdk中 ClassLoader对应的类名是AppClassLoader。如果你要看它的源码,建议下载openjdk查看,它位于sun.misc.Launcher命名空间下, 因为sunjdk无法直接查看到源码,只能通过反编译手段。最后来看User-Defined ClassLoader,该加载器是用户类加载器。开发人员可以通过 继承ClassLoader抽象了自己实现的Loader。用户自定义类加载器可以实现加载非Classpath中的类,例如可以用自定义类加载器加载网络上下载 的类。除此之外用户自定义类加载器还可以在加载类之前对类进行解密等操作。几种类加载器就简单的介绍到这里,最后,通过一个代码小片段,来获取下 程序中的类加载器。

public class LoadClassDemo {
    public static void main(String[] args) {
        System.out.println(LoadClassDemo.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@19821f
        System.out.println(LoadClassDemo.class.getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@addbf1
        System.out.println(LoadClassDemo.class.getClassLoader().getParent().getParent());//null
   }
}

我们可以看到输出,LoadClassDemo这个类的类加载器为AppClassLoader,这个应该没问题,因为我们知道,System ClassLoader加载启动参数中 Classpath中jar包和目录,而当前我们编译运行的这个类的Classpath中就包括该类文件。这个类加载器的父类,我们看到是ExtClassLoader,也就是 加载扩展jar包的类加载,而该加载器的父加载器却是null。因为该父加载器为Bootstrap ClassLoader,该加载器我们前面提到过,它不是java中的 ClassLoader,因此无法获取到。

小结

本篇简单介绍了jvm加载类的基本过程和几种类加载器。下一篇将继续深入了解ClassLoader。

##文档信息