Java的双亲委托类加载机制

双亲委托类加载机制

当我们使用java命令运行一个类时,并不是简单的运行的.class文件,把类加载到内存中,而是经过一系列的判断之后才加载类。以下将介绍Java的双亲委托类加载机制。

1. 加载器分类

  1. 启动类加载器(Aootstrap Class Loader)
    由C++编写,用来加载Java的核心库文件,主要是将JDK中jre/lib目录下的类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中。其本身属于JVM虚拟机的一部分,所以不能被Java代码中引用。
  2. 扩展类加载器(Extensions Class Loader)
    用来加载Java的扩展类(可以自行添加扩展类),主要是将jre/lib/etc下的类库加载到内存中。
  3. 应用程序类加载器(Application Class Loader)
    也叫系统加载类,是我们最常用的加载器,即环境变量内的CLASSPATH配置的路径。
  4. 自定义类加载器
    由用户编写的继承ClassLoader类的加载器。应用场景通常是加密和网络方面。本次不讨论。

类加载顺序

1. 思想

前提是被不同的类加载器加载的类一定是不同的类。设计思想主要是避免同一个类被不同的加载器加载,加载之前先查看父类是否已经加载,没有加载在尝试加载,如果还不可以才自己加载。加载顺序如下图:在这里插入图片描述

2. 有趣的实验

现在我们做一个有趣的实验来感受下类加载的过程。首先新建一个记事本,文件名及后缀名为MyTest9.java,编写下图代码。


public class MyTest9 {
	public static void main(String[] args) {
		System.out.println("程序员,很轻松的,就是头上有点凉。");
	}
}

然后cmd打开控制台,cd进入到此文件的目录,输入如下命令,会发现多出一个MyTest9.class和MyTest9.jar文件。


javac -encoding UTF-8  MyTest9.java
jar cvf MyTest9.jar MyTest9.class

在这里插入图片描述
重点来了我们把这个jar包放到C:\Program Files\Java\jre1.8.0_172\lib\ext下,注意,我使用的是JDK8,默认安装是有一个独立的jre的。
在这里插入图片描述
接着修改MyTest9.java文件,如下:


public class MyTest9 {
	public static void main(String[] args) {
		System.out.println("程序员,一块钱四个,嘿嘿......");
	}
}

最后,将使用javac -encoding UTF-8 MyTest9.java把MyTest9.java重新编译一次,输入java MyTest,输出结果如下:在这里插入图片描述
为什么不会输出程序员,一块钱四个,嘿嘿......这句呢,这大概就是双亲委托类加载机制。

启动器加载做了什么?

要查看启动类加载器做了什么就只能看源码了。本次查看的源码是OpenJDK8,请自行到官网下载。下载完源码解压后就使用反编译工具打开。这里使用的是Jd-gui。

public class Launcher {
    private static URLStreamHandlerFactory factory = new Factory();
    private static Launcher launcher = new Launcher();
    private static String bootClassPath =
        System.getProperty("sun.boot.class.path");

    public static Launcher getLauncher() {
        return launcher;
    }

    private ClassLoader loader;

    public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();//这里启动了扩展类加载器
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);//这里启动了应用程序类加载器
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        // Also set the context class loader for the primordial thread.
        Thread.currentThread().setContextClassLoader(loader);

        // Finally, install a security manager if requested
        String s = System.getProperty("java.security.manager");
        if (s != null) {
            SecurityManager sm = null;
            if ("".equals(s) || "default".equals(s)) {
                sm = new java.lang.SecurityManager();
            } else {
                try {
                    sm = (SecurityManager)loader.loadClass(s).newInstance();
                } catch (IllegalAccessException e) {
                } catch (InstantiationException e) {
                } catch (ClassNotFoundException e) {
                } catch (ClassCastException e) {
                }
            }
            if (sm != null) {
                System.setSecurityManager(sm);
            } else {
                throw new InternalError(
                    "Could not create SecurityManager: " + s);
            }
        }
    }

分别加载了什么类库?

如果想查看各个加载器到底加载了什么类库,可以使用以下代码查看。

package com.briup.ch25;

import java.util.Properties;

/**
 * 显示各种类加载器加载的类库
 */
public class MyTest8 {
	public static void main(String[] args) {
		// 获得所有系统属性
		Properties properties = System.getProperties();

		// 查看启动类加载器加载了什么
		String property = properties.getProperty("sun.boot.class.path");
		String[] split = property.split(";");
		for (String string : split) {
			System.out.println(string);
		}

		System.out.println("-------------------伟大的分割线----------------------");

		// 查看扩展类加载器加载了什么
		String property1 = properties.getProperty("java.ext.dirs");
		String[] split1 = property1.split(";");
		for (String string : split1) {
			System.out.println(string);
		}

		System.out.println("-------------------伟大的分割线----------------------");

		// 查看应用程序类加载器加载了什么
		String property2 = properties.getProperty("java.class.path");
		String[] split2 = property2.split(";");
		for (String string : split2) {
			System.out.println(string);
		}
	}
}

结果如下

C:\Program Files\Java\jdk1.8.0_172\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\sunrsasign.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_172\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_172\jre\classes
-------------------伟大的分割线----------------------
C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
-------------------伟大的分割线----------------------
F:\briup\work\spring-tool-suite-4-4.3.1.RELEASE-e4.12.0-win32.win32.x86_64\workspace\jd1908_corejava_hdy\bin
F:\briup\work\spring-tool-suite-4-4.3.1.RELEASE-e4.12.0-win32.win32.x86_64\workspace\jd1908_corejava_hdy\lib\lombok.jar
F:\briup\work\spring-tool-suite-4-4.3.1.RELEASE-e4.12.0-win32.win32.x86_64\workspace\jd1908_corejava_hdy\lib\springmvc.jar
F:\briup\work\spring-tool-suite-4-4.3.1.RELEASE-e4.12.0-win32.win32.x86_64\workspace\jd1908_corejava_hdy\lib\jdk-misc-2.Final.jar
F:\briup\work\spring-tool-suite-4-4.3.1.RELEASE-e4.12.0-win32.win32.x86_64\workspace\jd1908_corejava_hdy\lib\juniversalchardet-1.0.3.jar

总结

写这个类加载还是花了很多时间的,因为我也是个初学者,只是把很多前人的经验复刻一下,但还是从中学到了点什么。
参考博客
https://www.jianshu.com/p/353c26c744df
https://www.cnblogs.com/gc65/p/10170853.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值