Java类选择器 ——Thinking in Java学习笔记(五)

类选择器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

猜你喜欢

转载自blog.csdn.net/qq_40509039/article/details/83271796
今日推荐