java类加载器

梳理一下类加载器的机制以及原理。

什么是类加载器?

类加载器是一个用来加载类文件的类。Java源代码通过javac编译器编译成类文件。然后JVM来执行类文件中的字节码来执行程序。类加载器负责加载文件系统、网络或其他来源的类文件。有三种默认使用的类加载器:Bootstrap类加载器、Extension类加载器和System类加载器(也叫Application类加载器)。每种类加载器都有设定好从哪里加载类。

 

* BootStrap类加载器负责加载rt.jar中的JDK类文件。它是所有类加载器的父加载器。Bootstrap类加载器没有任何父类加载器,如果调用String.class.getClassLoader(),会返回null。Bootstrap类加载器被称为初始类加载器。

* Extension类加载器将加载类的请求先委托给父类加载器,也就是Bootstrap,如果没有成功加载的话,再从jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下加载类。Extension加载器由sun.misc.Launcher$ExtClassLoader实现。

* System类加载器(也叫Application类加载器),负责从classpath环境变量中加载某些应用相关的类,classpath环境变量通常由

-classpath或-cp命令行选项来定义,或者是JAR中的Manifest的classpath属性。Application类加载器是Extension类加载器的子加

载器,通过sun.misc.Launcher$AppClassLoader实现。

除了Bootstrap类加载器大部分是由C来写的,其他的类加载器都是通过java.lang.ClassLoader来实现的。

总结一下,三种类加载器加载类文件的地方:

1)Bootstrap类加载器 —— jre/lib/rt.jar

2)Extension类加载器 —— jre/lib/ext 或者 java.ext.dirs指向的目录

3)Application类加载器 —— classpath环境变量,由-claspath 或者 -cp选项定义,或者是JAR中的Manifest的classpath属性定义。

 

工作原理

Java类加载器的作用就是在运行时加载类。工作原理基于三个机制:委托、可见性、单一性。

委托机制

当一个类加载 和 初始化的时候,类仅在需要加载的时候被加载。假设有一个应用需要的类叫做A.class,首先加载这个类的请求由Application类加载器 委托 给它的父加载器Extension类加载器,然后再委托给Bootstrap类加载器。Bootstrap类加载器会先看看rt.jar中有没有这个类,因为并没有这个类,所以这个请求回到Extension类加载器,它会查看jre/lib/ext目录下有没有这个类,如果这个类被Extension类加载器找到了,那么它将被加载,而Application类加载器不会加载这个类;而如果这个类没有被Extension类加载器找到,那么再由Application类加载器从classpath中查找。记住classpath定义的是类文件的加载目录,而PATH定义的是可执行程序,如javac,java等的执行路径。

可见性机制

根据可见性机制,子类加载器可以看到父类加载器加载的类,反之则不行。所以下面的例子中,当A.class已经被Application类加载器加载过了,然后再使用Extension类加载器加载这个类,将会抛出java.lang.ClassNotFoundException异常。

package demo;


public class A {
 
        public static void main(String args[]) {
            try {
                ClassLoader cl = A.class.getClassLoader(); //会得到AppClassLoader 
        
                System.out.println("A.getClass().getClassLoader() : "+ cl);

                //cl.getParent()会得到ExtClassLoader
                Class.forName("demo.A", true,  cl.getParent());

            } catch (ClassNotFoundException ex) {
                Log.e(A.class.getName(), ex);
            }
        }
 
    }

单一性机制

根据这个机制,父类加载器加载过的类不能被子类加载器加载第二次。虽然可以重写违反 委托 和 单一性机制的类加载器,但这样做并不可取。

 

如何显示的加载类

Java提供了显式加载类的API:Class.forName(classname) 和 Class.forName(classname, initialized, classloader)。就行上面例子中,可以指定类加载器的名称 以及 要加载的类的名称。类的加载是通过调用java.lang.ClassLoader的loadClass()方法,而loadClass()方法则调用了findClass()方法来定位相应类的字节码。如果findClass()没有找到,那么它会抛出java.lang.ClassNotFoundException异常,如果找到的话则会调用defineClass()将字节码转化成类实例,然后返回。

 

最后,能不能自己写个类叫java.lang.System?

答:通常情况下不可以。但是可以采取另类方法达到这个需求。

解释:为了不让我们写System类,类加载采用委托机制,这样可以保证父类优先,父类能加载的类,子类久没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是会使用java系统提供的System,自己写的System类根本没有机会得到加载。

但是我们可以自定义一个类加载器来达到这个目的,为了避免委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己定义的类加载器放在一个特殊的目录,那么系统的类加载器就无法加载,也就是最终还是要由我们自定义的加载器来加载。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值