类选择器ClassLoader时什么
java程序运行时,是先由javac编译器将java文件编译成.class文件,然后在jvm中对.class文件进行类加载。
类选择器的功能就是负责读取.class字节码文件,并将其转换成java.lang.Class类的一个实例。
每个实例用来表示一个java类。通过该实例的newInstance()方法可以创建出一个该类的对象。
ClassLoader的种类
JDK 默认提供了三种默认类选择器:BootstrpLoader、ExtClassLoader 、AppClassLoader
1、BootstrpLoader
BootstrpLoader是用C++语言写的,它称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,可通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件:
2、ExtClassLoader
BootstrpLoader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader。
ExtClassLoader是用Java写的,它称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。
3、AppClassLoader
BootstrpLoader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。
AppClassLoader也是用Java写成的,它的实现类是sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader。
AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
举个例子:
Public class Test {
Public static void main(String[] arg){
ClassLoader c = Test.class.getClassLoader(); //获取Test类的类加载器
System.out.println(c);
ClassLoader c1 = c.getParent(); //获取c这个类加载器的父类加载器
System.out.println(c1);
ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2);
}
}
运行结果:
……AppClassLoader……
……ExtClassLoader……
Null
可以看出Test是由AppClassLoader加载器加载的,AppClassLoader的Parent 加载器是 ExtClassLoader,但是ExtClassLoader的Parent为 null 是怎么回事呵,朋友们留意的话,前面有提到BootstrpLoader是用C++语言写的,依java的观点来看,逻辑上并不存在BootstrpLoader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null。
4、自定义类选择器
除了Java默认提供的三个ClassLoader之外,用户还可以根据需要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类,也包括Java提供的另外二个ClassLoader(ExtClassLoader和AppClassLoader)在内。
但是BootstrpLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,BootstrpLoader也随着启动,负责加载完核心类库后,并构造ExtClassLoader和AppClassLoader类加载器。
双亲委派模型
类加载器 Java 类如同其它的 Java 类一样,也是要由类加载器来加载的;除了启动类加载器,每个类都有其父类加载器(父子关系由组合(不是继承)来实现);
所谓双亲委派是指每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载。
双亲委派好处
1、避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
2、更加安全,防止自己重写java核心api中定义类型。如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。
3、每个加载器只能加载自己范围内的类;
定义自已的ClassLoader
既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?
因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。
定义自已的类加载器分为两步:
1、继承java.lang.ClassLoader
2、重写父类的findClass方法
读者可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?
因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。
参考材料:
1、https://www.cnblogs.com/aspirant/p/7200523.html
2、https://www.cnblogs.com/doit8791/p/5820037.html
3、https://blog.csdn.net/slim68/article/details/70849101
4、https://www.cnblogs.com/fingerboy/p/5456371.html