JVM 类的创建,类加载器的初始化,双亲委派机制—1

类的执行流程:
在这里插入图片描述
JVM实际只是我们用来排查内存溢出和多线程下CPU飙高的一个工具
类的加载过程:

package com.kirn.interfaceTest.jvm;

import com.kirn.entity.User;

/**
 * 首先简单描述类加载的的机制
 *loadClass类加载机制
 * 加载-->验证-->准备-->解析-->初始化-->使用-->卸载
 *
 * 1.加载:在硬盘上查找并通过IO读入字节码文件,使用类的时候才会加载,例如调用类的Main方法,new对象等等,在加载阶段会在内存里面生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
 * 2.验证:校验字节码文件的正确性
 * 3.准备:给类的静态变量分配内存,并赋予默认值
 * 4.解析:将符号引用替换为直接引用,该阶段会把一些静态方法  (符号引用,比如main方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态连接过程(类加载期间完成),动态连接是在程序运行期间完成的将符号引用替换为直接引用
 * 5.初始化: 对类的静态变量初始化为指定的值,执行静态代码块
 *
 *
 * 类被加载到方法区中后主要包含 运行时常量池、类型信息、字段信息、方法信息、类加载器的
 * 引用、对应class实例的引用等信息。
 * 类加载器的引用:这个类到类加载器实例的引用
 * 对应class实例的引用:类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的
 * 对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。
 * 注意,主类在运行过程中如果使用到其它类,会逐步加载这些类。
 * jar包或war包里的类不是一次性全部加载的,是使用到时才加载。
 *
 *
 * @author fm
 * @date 2022/5/14
 */
public class Math {
    public static final int initData = 666;
    public static User user = new User();
    public int compute() { //一个方法对应一块栈帧内存区域
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;

    }

    public static void main(String[] args) {

        Math math = new Math();
         int compute = math.compute();
        System.out.println(compute);
    }
}

代码的执行流程:

package com.kirn.interfaceTest.jvm;

/**
 * 注意,主类在运行过程中如果使用到其它类,会逐步加载这些类。
 * jar包或war包里的类不是一次性全部加载的,是使用到时才加载
 * @author fm
 * @date 2022/5/14
 */
public class TestDynamicLoad {

    static {
        System.out.println("***************test1*****************");
    }

    public static void main(String[] args) {
        new A();
        System.out.println("*****************load test********************");
        //此处B不会加载,除非这里是new B();
        B b=null;
    }


}
class A{
    static {
        System.out.println("**********load A*******************");
    }

    public A(){
        System.out.println("*************load A方法****************");
    }
}


class B{
    static {
        System.out.println("***************load B***************");
    }

    public B(){
        System.out.println("*************load B方法****************");
    }
}

类加载器和双亲委派机制:

package com.kirn.interfaceTest.jvm;

import sun.misc.Launcher;

import java.net.URL;

/**
 * 1.引导类加载器Bootstrap ClassLoader:
 * 负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如:rt.jar、charsets.jar等;
 * 2.扩展类加载器extensions Classloade:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包;
 * 3.应用程序类加载器system Classloader:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类;
 * 4.自定义加载器:负责加载用户自定义路径下的类包;
 * @author fm
 * @date 2022/5/14
 */
public class TestJDKClassLoader {
    public static void main(String[] args) {
        //引导类加载的String类  返回为null
        System.out.println(String.class.getClassLoader());
        //DESKeyFactory这个类是属于扩展类里面的  也就是jre的lib目录下的ext扩展目录里面的包
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
        //这个类TestJDKClassLoader就是奔类,是我们自己实现的
        System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());
        System.out.println("===================================");
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extClassloader = appClassLoader.getParent();
        ClassLoader bootstrapLoader = extClassloader.getParent();
        System.out.println("the bootstrapLoader : " + bootstrapLoader);
        System.out.println("the extClassloader : " + extClassloader);
        System.out.println("the appClassLoader : " + appClassLoader);
        System.out.println();
        System.out.println("bootstrapLoader加载以下文件:");
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();

        for (int i = 0; i < urls.length; i++) {

            System.out.println(urls[i]);

        }
        System.out.println();

        System.out.println("extClassloader加载以下文件:");

        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println();
        System.out.println("appClassLoader加载以下文件:");
        System.out.println(System.getProperty("java.class.path"));

    }
}

打印结果:
bootstrapLoader加载以下文件:
file:/F:/JDK1.8/jre/lib/resources.jar
file:/F:/JDK1.8/jre/lib/rt.jar
file:/F:/JDK1.8/jre/lib/sunrsasign.jar
file:/F:/JDK1.8/jre/lib/jsse.jar
file:/F:/JDK1.8/jre/lib/jce.jar
file:/F:/JDK1.8/jre/lib/charsets.jar
file:/F:/JDK1.8/jre/lib/jfr.jar
file:/F:/JDK1.8/jre/classes

extClassloader加载以下文件:
F:\JDK1.8\jre\lib\ext;C:\Windows\Sun\Java\lib\ext

appClassLoader加载以下文件:
F:\JDK1.8\jre\lib\charsets.jar;F:\JDK1.8\jre\lib\deploy.jar;F:\JDK1.8\jre\lib\ext\access-bridge-64.jar;F:\JDK1.8\jre\lib\ext\cldrdata.jar;F:\JDK1.8\jre\lib\ext\dnsns.jar;F:\JDK1.8\jre\lib\ext\jaccess.jar;F:\JDK1.8\jre\lib\ext\jfxrt.jar;F:\JDK1.8\jre\lib\ext\localedata.jar;F:\JDK1.8\jre\lib\ext\nashorn.jar;F:\JDK1.8\jre\lib\ext\sunec.jar;F:\JDK1.8\jre\lib\ext\sunjce_provider.jar;F:\JDK1.8\jre\lib\ext\sunmscapi.jar;F:\JDK1.8\jre\lib\ext\sunpkcs11.jar;F:\JDK1.8\jre\lib\ext\zipfs.jar;F:\JDK1.8\jre\lib\javaws.jar;F:\JDK1.8\jre\lib\jce.jar;F:\JDK1.8\jre\lib\jfr.jar;F:\JDK1.8\jre\lib\jfxswt.jar;F:\JDK1.8\jre\lib\jsse.jar;F:\JDK1.8\jre\lib\management-agent.jar;F:\JDK1.8\jre\lib\plugin.jar;F:\JDK1.8\jre\lib\resources.jar;F:\JDK1.8\jre\lib\rt.jar;F:\spring_demotest\spring_code_demo\target\test-classes;F:\spring_demotest\spring_code_demo\target\classes;E:\repository\org\springframework\boot\spring-boot-starter-web\2.1.6.RELEASE\spring-boot-starter-web-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-starter\2.1.6.RELEASE\spring-boot-starter-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot\2.1.6.RELEASE\spring-boot-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.6.RELEASE\spring-boot-autoconfigure-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-starter-logging\2.1.6.RELEASE\spring-boot-starter-logging-2.1.6.RELEASE.jar;E:\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;E:\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;E:\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;E:\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;E:\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar;E:\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;E:\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;E:\repository\org\springframework\boot\spring-boot-starter-json\2.1.6.RELEASE\spring-boot-starter-json-2.1.6.RELEASE.jar;E:\repository\com\fasterxml\jackson\core\jackson-databind\2.9.9\jackson-databind-2.9.9.jar;E:\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;E:\repository\com\fasterxml\jackson\core\jackson-core\2.9.9\jackson-core-2.9.9.jar;E:\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.9\jackson-datatype-jdk8-2.9.9.jar;E:\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.9\jackson-datatype-jsr310-2.9.9.jar;E:\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.9\jackson-module-parameter-names-2.9.9.jar;E:\repository\org\springframework\boot\spring-boot-starter-tomcat\2.1.6.RELEASE\spring-boot-starter-tomcat-2.1.6.RELEASE.jar;E:\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.21\tomcat-embed-core-9.0.21.jar;E:\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.21\tomcat-embed-el-9.0.21.jar;E:\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.21\tomcat-embed-websocket-9.0.21.jar;E:\repository\org\hibernate\validator\hibernate-validator\6.0.17.Final\hibernate-validator-6.0.17.Final.jar;E:\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;E:\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;E:\repository\org\springframework\spring-web\5.1.8.RELEASE\spring-web-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-beans\5.1.8.RELEASE\spring-beans-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-webmvc\5.1.8.RELEASE\spring-webmvc-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-aop\5.1.8.RELEASE\spring-aop-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-context\5.1.8.RELEASE\spring-context-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-expression\5.1.8.RELEASE\spring-expression-5.1.8.RELEASE.jar;E:\repository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar;E:\repository\org\springframework\boot\spring-boot-starter-jdbc\2.1.6.RELEASE\spring-boot-starter-jdbc-2.1.6.RELEASE.jar;E:\repository\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;E:\repository\org\springframework\spring-jdbc\5.1.8.RELEASE\spring-jdbc-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-tx\5.1.8.RELEASE\spring-tx-5.1.8.RELEASE.jar;E:\repository\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;E:\repository\tk\mybatis\mapper-spring-boot-starter\2.1.5\mapper-spring-boot-starter-2.1.5.jar;E:\repository\org\mybatis\mybatis\3.4.6\mybatis-3.4.6.jar;E:\repository\org\mybatis\mybatis-spring\1.3.2\mybatis-spring-1.3.2.jar;E:\repository\tk\mybatis\mapper-core\1.1.5\mapper-core-1.1.5.jar;E:\repository\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar;E:\repository\tk\mybatis\mapper-base\1.1.5\mapper-base-1.1.5.jar;E:\repository\tk\mybatis\mapper-weekend\1.1.5\mapper-weekend-1.1.5.jar;E:\repository\tk\mybatis\mapper-spring\1.1.5\mapper-spring-1.1.5.jar;E:\repository\tk\mybatis\mapper-extra\1.1.5\mapper-extra-1.1.5.jar;E:\repository\tk\mybatis\mapper-spring-boot-autoconfigure\2.1.5\mapper-spring-boot-autoconfigure-2.1.5.jar;E:\repository\org\springframework\boot\spring-boot-starter-test\2.1.6.RELEASE\spring-boot-starter-test-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-test\2.1.6.RELEASE\spring-boot-test-2.1.6.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.1.6.RELEASE\spring-boot-test-autoconfigure-2.1.6.RELEASE.jar;E:\repository\com\jayway\jsonpath\json-path\2.4.0\json-path-2.4.0.jar;E:\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;E:\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;E:\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;E:\repository\junit\junit\4.12\junit-4.12.jar;E:\repository\org\assertj\assertj-core\3.11.1\assertj-core-3.11.1.jar;E:\repository\org\mockito\mockito-core\2.23.4\mockito-core-2.23.4.jar;E:\repository\net\bytebuddy\byte-buddy\1.9.13\byte-buddy-1.9.13.jar;E:\repository\net\bytebuddy\byte-buddy-agent\1.9.13\byte-buddy-agent-1.9.13.jar;E:\repository\org\objenesis\objenesis\2.6\objenesis-2.6.jar;E:\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;E:\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;E:\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;E:\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;E:\repository\org\springframework\spring-core\5.1.8.RELEASE\spring-core-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-jcl\5.1.8.RELEASE\spring-jcl-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-test\5.1.8.RELEASE\spring-test-5.1.8.RELEASE.jar;E:\repository\org\xmlunit\xmlunit-core\2.6.2\xmlunit-core-2.6.2.jar;E:\repository\org\springframework\boot\spring-boot-starter-data-redis\2.1.6.RELEASE\spring-boot-starter-data-redis-2.1.6.RELEASE.jar;E:\repository\org\springframework\data\spring-data-redis\2.1.9.RELEASE\spring-data-redis-2.1.9.RELEASE.jar;E:\repository\org\springframework\data\spring-data-keyvalue\2.1.9.RELEASE\spring-data-keyvalue-2.1.9.RELEASE.jar;E:\repository\org\springframework\data\spring-data-commons\2.1.9.RELEASE\spring-data-commons-2.1.9.RELEASE.jar;E:\repository\org\springframework\spring-oxm\5.1.8.RELEASE\spring-oxm-5.1.8.RELEASE.jar;E:\repository\org\springframework\spring-context-support\5.1.8.RELEASE\spring-context-support-5.1.8.RELEASE.jar;E:\repository\io\lettuce\lettuce-core\5.1.7.RELEASE\lettuce-core-5.1.7.RELEASE.jar;E:\repository\io\netty\netty-common\4.1.36.Final\netty-common-4.1.36.Final.jar;E:\repository\io\netty\netty-handler\4.1.36.Final\netty-handler-4.1.36.Final.jar;E:\repository\io\netty\netty-buffer\4.1.36.Final\netty-buffer-4.1.36.Final.jar;E:\repository\io\netty\netty-codec\4.1.36.Final\netty-codec-4.1.36.Final.jar;E:\repository\io\netty\netty-transport\4.1.36.Final\netty-transport-4.1.36.Final.jar;E:\repository\io\netty\netty-resolver\4.1.36.Final\netty-resolver-4.1.36.Final.jar;E:\repository\io\projectreactor\reactor-core\3.2.10.RELEASE\reactor-core-3.2.10.RELEASE.jar;E:\repository\org\reactivestreams\reactive-streams\1.0.2\reactive-streams-1.0.2.jar;E:\repository\cn\afterturn\easypoi-base\3.2.0\easypoi-base-3.2.0.jar;E:\repository\org\apache\poi\poi\3.15\poi-3.15.jar;E:\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;E:\repository\org\apache\commons\commons-collections4\4.1\commons-collections4-4.1.jar;E:\repository\org\apache\poi\poi-ooxml\3.15\poi-ooxml-3.15.jar;E:\repository\com\github\virtuald\curvesapi\1.04\curvesapi-1.04.jar;E:\repository\org\apache\poi\poi-ooxml-schemas\3.15\poi-ooxml-schemas-3.15.jar;E:\repository\org\apache\xmlbeans\xmlbeans\2.6.0\xmlbeans-2.6.0.jar;E:\repository\stax\stax-api\1.0.1\stax-api-1.0.1.jar;E:\repository\com\google\guava\guava\16.0.1\guava-16.0.1.jar;E:\repository\org\apache\commons\commons-lang3\3.8.1\commons-lang3-3.8.1.jar;E:\repository\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar;E:\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;E:\repository\cn\afterturn\easypoi-web\3.2.0\easypoi-web-3.2.0.jar;E:\repository\cn\afterturn\easypoi-annotation\3.2.0\easypoi-annotation-3.2.0.jar;E:\repository\c3p0\c3p0\0.9.1.2\c3p0-0.9.1.2.jar;E:\repository\com\alibaba\druid\1.1.22\druid-1.1.22.jar;E:\idea\IntelliJ IDEA 2020.3.2\lib\idea_rt.jar


类加载器初始化过程:
参见类运行加载全过程图可知其中会创建JVM启动器实例sun.misc.Launcher。sun.misc.Launcher初始化使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。
在Launcher构造方法内部,其创建了两个类加载器,分别是
sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)。
JVM默认使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例加载我们的应用程序
源码:

 public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                //构造扩展类加载器,实则就是初始化一个扩展类加载器出来
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }

            try {
                //构造应用类加载器,在构造的过程中将其父加载器设置为ExtClassLoader,
                //Launcher的loader属性值是AppClassLoader,我们一般都是用这个类加载器来加载我们自己写的应用程序
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }

            Thread.currentThread().setContextClassLoader(this.loader);
            String var2 = System.getProperty("java.security.manager");
            if (var2 != null) {
                SecurityManager var3 = null;
                if (!"".equals(var2) && !"default".equals(var2)) {
                    try {
                        var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                    } catch (IllegalAccessException var5) {
                    } catch (InstantiationException var6) {
                    } catch (ClassNotFoundException var7) {
                    } catch (ClassCastException var8) {
                    }
                } else {
                    var3 = new SecurityManager();
                }

                if (var3 == null) {
                    throw new InternalError("Could not create SecurityManager: " + var2);
                }

                System.setSecurityManager(var3);
            }

证明初始化就是new一个扩展类加载器的源码:
在这里插入图片描述

双亲委派机制:
在这里插入图片描述
1.这里类加载其实就有一个双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

2.比如我们的Math类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载器加载,扩展类加载器再委托引导类加载器,顶层引导类加载器在自己的类加载路径里找了半天没找到Math类,则向下退回加载Math类的请求,扩展类加载器收到回复就自己加载,在自己的类加载路径里找了半天也没找到Math类,又向下退回Math类的加载请求给应用程序类加载器,应用程序类加载器于是在自己的类加载路径里找Math类,结果找到了就自己加载了。。
3.双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载;

源码解析类加载器:

 System.out.println("双亲委派机制执行过程源码");
        /**
         * 1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
         * 2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,
         * 则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
         * 3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
         */
        protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                //检查这个类是否已被当前类加载器加载过
                Class<?> c = findLoadedClass(name);
                //如果被加载过,已存在则直接返回,如果没有则为null,为空走if方法内的逻辑
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            //这里是交给父类加载器也就是扩展类加载器去加载
                            c = parent.loadClass(name, false);
                        } else {
                            //这里是交给引导类加载器去加载
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }

                    //如果都没有查到被加载过的类,那么就会走这段逻辑
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        //都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类
                        c = findClass(name);

                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                //返回加载的类
                return c;
            }
        }

在这里插入图片描述

为什么要设计双亲委派机制?
1.沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改;
2.避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性;

示例:

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

        }
        运行结果:
        错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
        public static void main(String[] args)
        否则 JavaFX 应用程序类必须扩展javafx.application.Application
首先:我们的java.lang.String属于核心类包,应该被引导类顶级类加载器加载,这样就是为了防止我们的核心API被随意篡改也确保了唯一性;

自定义类加载器:
自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。
在这里插入图片描述

package com.kirn.interfaceTest.jvm;

import java.io.FileInputStream;
import java.lang.reflect.Method;

/**
 * 自定义的实现流程:
 * 自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是
 * loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空
 * 方法,所以我们自定义类加载器主要是重写findClass方法
 *
 * loadClass方法是双亲委派机制的实现
 * 双亲委派机制:比如我们请求一个Math类的时候,它会找到应用程序类加载器,此时应用程序类加载器会
 * 查询本身有没有加载过此类,如果没有,那么就会委托给父类加载器扩展类加载器,扩展类加载器查询本身
 * 有没有加载此类,若没有,委托给顶级类加载器引导类加载器去加载,如果引导类加载器查询自己本身没有加载此类
 * 且在它的加载的jre的jar内没有,那么就会委托给扩展类加载器去加载,如果扩展类加载器查询它的jre的lib目录下没有此类
 * 那么就会委托给应用程序类加载器去加载,然后应用程序类加载器就会加载此类
 *
 *
 * 自定义类加载器实则可以视为它的父类属于应用程序类加载器,也是实现了双亲委派原则
 * @author fm
 * @date 2022/5/15
 */
public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;
        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }

        /**
         * 将类路径加载进入
         * @param name
         * @return
         * @throws Exception
         */
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");

            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;

        }

        /**
         * 将我们自己的类加载
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }

        }
    }
    public static void main(String args[]) throws Exception {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        //D盘创建 test/com/kirin/entity 几级目录,将User类的复制类User.class丢入该目录
        Class clazz = classLoader.loadClass("com.kirin.entity.User");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("contract", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }

}


运行结果:
=======自己的加载器加载类调用方法=======
com.tuling.jvm.MyClassLoaderTest$MyClassLoader

打破双亲委派机制:

只需要重写loadClass方法即可,因为双亲委派机制的逻辑都是这个方法实现的;

package com.kirn.interfaceTest.jvm;

import java.io.FileInputStream;
import java.lang.reflect.Method;

import static io.lettuce.core.internal.LettuceClassUtils.findClass;

/**
 * 自定义的实现流程:
 * 自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是
 * loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空
 * 方法,所以我们自定义类加载器主要是重写findClass方法
 *
 * loadClass方法是双亲委派机制的实现
 * 双亲委派机制:比如我们请求一个Math类的时候,它会找到应用程序类加载器,此时应用程序类加载器会
 * 查询本身有没有加载过此类,如果没有,那么就会委托给父类加载器扩展类加载器,扩展类加载器查询本身
 * 有没有加载此类,若没有,委托给顶级类加载器引导类加载器去加载,如果引导类加载器查询自己本身没有加载此类
 * 且在它的加载的jre的jar内没有,那么就会委托给扩展类加载器去加载,如果扩展类加载器查询它的jre的lib目录下没有此类
 * 那么就会委托给应用程序类加载器去加载,然后应用程序类加载器就会加载此类
 *
 *
 * 自定义类加载器实则可以视为它的父类属于应用程序类加载器,也是实现了双亲委派原则
 * @author fm
 * @date 2022/5/15
 */
public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;
        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }

        /**
         * 将类路径加载进入
         * @param name
         * @return
         * @throws Exception
         */
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");

            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;

        }

        /**
         * 将我们自己的类加载
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }

        }
    }

    /**
     * 打破双亲委派机制:
     * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
     * @param name
     * @param resolve
     * @return
     * @throws ClassNotFoundException
     */
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    /**
                     * 我们自定义加载的时候,实现了双亲委派机制,如果应用类加载器没有加载过我们自定义的类,那么就会直接走我们自定义的类加载器去加载
                     * 如果应用类加载器里面有我们的自定义类,通常顶级类加载器引导类加载器就会去加载核心类Object.class
                     * 所以此处就会报错,解决就是加以下代码判断逻辑
                     *
                     * 如果不是我们自己的类,就让他继续执行双亲委派机制,如果是我们指定包目录下的类,就走我们自定义类加载器即可
                     */
                    if (!name.startsWith("com.kirin")){
                        this.getParent().loadClass(name);
                    }else {
                        c = findClass(name);
                    }


                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


    public static void main(String args[]) throws Exception {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        //D盘创建 test/com/kirin/entity 几级目录,将User类的复制类User.class丢入该目录
        Class clazz = classLoader.loadClass("com.kirin.entity.User");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("contract", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值