类加载器子系统——JVM之四

一、类加载器基本概念

  顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。但第二次实例化一个类时,就从对应Class类newInstance(),不用每次都读取.class文件。 

二、类加载过程

一、JVM将整个类加载过程划分为了三个步骤:

(1)装载

      装载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载,同样,也采用以上三个元素来标识一个被加载了的类:类名+包名+ClassLoader实例ID,因此不同类加载器加载相同的类是不同的。有继承,将先在加载父类。

(2)链接

      链接过程负责对二进制字节码的格式进行:校验、解析类中调用的接口、类。校验是防止不合法的.class文件,然后 对类中的所有属性、调用方法进行解析,以确保其需要调用的属性、方法存在,以及具备应的权限(例如public、private域权限等),会造成NoSuchMethodError、NoSuchFieldError等错误信息。

(3)初始化

      初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化。

  在四种情况下初始化过程会被触发执行:

      调用了new;

  反射调用了类中的方法;

  子类调用了初始化(先执行父类静态代码和静态成员,再执行子类静态代码和静态变量,然后调用父类构造器,最后调用自身构造器。);

  JVM启动过程中指定的初始化类。 

三、类加载器的代理模式

  类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。

具体过程详见《http://baike.baidu.com/link?url=AN43X7Jb8MPxU8gX3-I5Twu0-YjqRz--cWMOQjOue3uMAh-8kELZC0pZpBfVTpeuVJ_YtxUczSGcpqDE69yNwq》。类推顺序如下:

类加载器子系统——JVM之四

注意这不是继承关系,而是代理关系。 主要分为以下几类:

(1) Bootstrap ClassLoader

      这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。

(2) Extension ClassLoader

     JVM用此classloader来加载扩展功能的一些jar包

(3) System ClassLoader或者AppClassLoader

      JVM用此classloader来加载启动参数中指定的Classpath中的jar包以及目录,在Sun JDK中ClassLoader对应的类名为AppClassLoader。

(4) User-Defined ClassLoader

       User-DefinedClassLoader是Java开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录

注意:

1、所有的类加载器都继承与ClassLoader类。并且BootstrapClassLoader、ExtClassLoader、AppClassLoader继承于Java.net.URLClassLoader,可以从本地和网上下载字节码。URLClassLoader继承ClassLoader。;

2、BootstrapClassLoader是最先加载的,它然后依次加载出ExtClassLoader、AppClassLoader对象。

四、Class类

      Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
      Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

(一) 如何得到Class的对象呢?有三种方法可以的获取:
    1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。例如:

    MyObject x;
    Class c1 = x.getClass();
    2、使用Class类的中静态forName()方法获得与字符串对应的Class对象。例如: 
    Class c2=Class.forName("MyObject"),Employee必须是接口或者类的名字。
    3、获取Class类型对象的第三个方法非常简单。如果T是一个Java类型,那么T.class就代表了匹配的类对象。例如
    Class cl1 = Manager.class;
    Class cl2 = int.class;
    Class cl3 = Double[].class;
    注意:Class对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的int.class是一个Class类型的对象。由于历史原因,数组类型的getName方法会返回奇怪的名字。
(二) Class类的常用方法
    1、getName() 
    一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
    2、newInstance()
    Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。例如:
    x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
    3、getClassLoader() 
    返回该类的类加载器。
    4、getComponentType() 
    返回表示数组组件类型的 Class。
    5、getSuperclass() 
    返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
    6、isArray() 
    判定此 Class 对象是否表示一个数组类。
(三) Class的一些使用技巧
    1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例如
    Object obj = Class.forName(s).newInstance();
    2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象。例如:
    if(e.getClass() == Employee.class)...
(四)Class的详细方法 
 
 
参考文章:
   全面详细内容参考:IBM官方http://www.ibm.com/developerworks/cn/java/j-lo-classloader/