类的加载机制-ClassLoader

    ClassLoader :public abstract class ClassLoader  ,  extends Object

    类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载
会试图查找出或生成构成类定义数据。一般策略是将名称转换为某个文件,然后从文件系统读取该名称的 
“文件”,每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。数组类的Class对象不是由类加载
创建的,而是由Java运行时根据需要自动创建。数组类的类加载器由Class.getClassLoader()返回,该加载
器与其元素类型的类加载器是相同的;如果该元素是基本类型,则该数组类没有类加载器。

    应用程序需要实现ClassLoader的子类,以扩展Java虚拟机动态加载类的方式。

    类加载器通常由安全管理器使用,用于指示安全域。

    ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。
需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父
类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它
用作 ClassLoader 实例的父类加载器。

    通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚
拟机从 CLASSPATH 环境变量定义的目录中加载类。

    然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。
defineClass 方法将一个 byte 数组转换为 Class 类的实例。这种新定义的类的实例可以使用
Class.newInstance 来创建。

    类加载器所创建对象的方法和构造方法可以引用其他类。为了确定引用的类,Java 虚拟机将调用最初创建
该类的类加载器的 loadClass 方法。

    ClassLoader的类别:
        *启动类加载器(BootStrap)
        *扩展类加载器(Extension)
        *应用程序类加载器(AppClassLoader)
        *用户自定义加载器

    一般我们自己写的类用的都是AppClassLoader类加载器,就是下面的测试结果中,
获取自己写的类的类加载器是 sun.misc.Launcher$AppClassLoader@18b4aac2,而获取String的
就是null,这个null就是BootStrap这个加载器,BootStrap类加载器相当于启动类加载器、
应用程序类加载器的祖宗,若是用了BootStrap,由于BootStrap上一级已经没有了,所以就用“null”来表示



                        |——————————————————————|
                        |BootStrap ClassLoader |  $JAVA_HOME/jre/lib/rt.jar
                        |——————————————————————|
                                    |
                        |——————————————————————|
                        |Extension ClassLoader |  $JAVA_HOME/jre/lib/*.jar
                        |——————————————————————|
                                    |
                        |——————————————————————|
                        |System ClassLoader    |  $CLASSPATH
                        |——————————————————————|
                           |              |
                          |                |
      |————————————————————————|        |————————————————————————|
      |User-Defined ClassLoader|        |User-Defined ClassLoader|
      |————————————————————————|        |————————————————————————|
                  |
                  |
      |————————————————————————|
      |User-Defined ClassLoader|
      |————————————————————————|

    这张图就可以很清晰得看到:
1.所有在$Java_Home/jre/lib/rt.jar是通过BootStrap加载的
2.所有在$Java_Home/jre/lib/ext/*.jar是通过Extension加载的
3.所有在$CLASSPATH是通过SYSTEM加载的(应用程序类加载器也叫系统类加载器,加载当前应用的classpath 
  的所有类)


举个例子:创建一个java.lang包,然后创建String类,打印一句话执行

package java.lang; 
public class String{ 
    public static void main(String[] args){
        System.out.println("Hello World");
    }
}

    执行后报错:错误:在类java.lang.String中找不到main方法,请将main方法定义为public static
 void main(String[] args),否则JavaFX应用程序类必须扩展javafx.application.Application

    出现这种情况,涉及到双亲委派机制:

    当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,
每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己
无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
 
    所以他的实际运行过程是这样的:
ClassLoader收到String类的加载请求,先去BootStrap类中查找是否有这个类,没有则反馈无法完成这个请
求,但是在rt.jar中找到了java.lang.String这个类,执行这个类,这个类中没有定义main方法
    
    所以上面的例子,他会找到jdk中java.lang.String这个类,这个类确实没有main方法,简单说它执行了
的类是JDK中java.lang.String这个类,而不是我们自己定义的类

    双亲委派机制的优点:采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,
不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载
器最终得到的都是同样一个 Object对象。

猜你喜欢

转载自blog.csdn.net/csdnbeyoung/article/details/105264704