java 类加载机制

java main() 启动流程

1.在启动类的class 利用java的环境启动jvm,创建javaJVM虚拟机
2.创建jvn根类加载器底层由C++实现
3.由引导类加载器创建其他的类加载器(扩展类加载器,应用加载器,自定义加载器)
4.利用类加载器实现的loadClass 加载当前calss,执行main方法
5.执行完毕,销毁

在这里插入图片描述

类加载的过程(类加载器属于lazy,如果只是定义,并没有使用就不会加载 如 Demo demo = null)

  1. 加载
    将磁盘或者网络加载到jvm内存中,

  2. 验证
    验证class字节码头信息,二进制是否符合java的字节码规范

  3. 准备
    将静态变量赋值=(默认值)

  4. 解析
    将符合引用替换为直接引用。就是把代码转换为静态的常量自变量符合,静态链接过程。动态链接只有在运行是才会把代码符合找到对应的代码,如接口,多态等在执行时,才知道,这个过程就是动态链接

  5. 初始化
    给静态变量赋真实的值 ,常量直接进行赋值

  6. 使用

  7. 卸载

类加载器分类

  • 引导类加载器 加载自身自带的核心类,例如String.class

  • 扩展类加载器 加载扩展库(ext包下的jar包) 如 DESKeyFactory.calss

  • 应用类加载器 加载应用程序代码 如自己定义的 Demo.class

  • 自定义加载器

双亲委派机制

是指加载某一个类时,委托自己父类加载器进行加载,若有则返回,没有继续向上委托,如果到引用加载器都没有找到,在由自己的加载器进行类的加载。

在这里插入图片描述

为什么要使用双亲委派机制

  1. 为了安全,防止本身项目使用jdk一样的包名和类名将jdk代码覆盖。
  2. 避免重复加载。父类加载了,子类不需要加载,确保类的唯一性

自定义类加载器示

  1. 基础ClassLoad

  2. 重写findClass方法。

    创建测试类

package com.moon;

import lombok.NoArgsConstructor;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName MyTestClassLoad
 * @Author moon
 * @Date 2022/8/8
 * @Description 自义定classLoad
 * @Version 1.0
 */
@NoArgsConstructor
public class MyTestClassLoad extends ClassLoader {
    private String path;

    public MyTestClassLoad(String path) {
        this.path = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {


        byte[] classBytes = loadName(name);
        return defineClass(name, classBytes, 0, classBytes.length);
    }

    private byte[] loadName(String name) {
        name = name.replaceAll("\\.", "/");
        try (FileInputStream fileInputStream = new FileInputStream(path + "/" + name + ".class");) {
            int available = fileInputStream.available();
            byte[] bytes = new byte[available];
            fileInputStream.read(bytes);
            return bytes;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


        return null;
    }


    @Override
    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 (!name.startsWith("user")) {
                        c = this.getParent().loadClass(name);
                    } else {
                        c = findClass(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();
                    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 {
        MyTestClassLoad classLoad = new MyTestClassLoad("E:\\user1");
        Class<?> mindao = classLoad.loadClass("com.moon.entity.User");
        Object o = mindao.newInstance();
        Method setControlId = mindao.getDeclaredMethod("setAge", int.class);
        setControlId.invoke(o, 10);
        System.out.println(mindao.getDeclaredMethod("getAge").invoke(o));
        System.out.println(mindao.getClassLoader());
        System.out.println("--------------------------");
        System.out.println(mindao.getClassLoader());
        classLoad = new MyTestClassLoad("E:\\user2");
        mindao = classLoad.loadClass("com.moon.entity.User");
        o = mindao.newInstance();
        setControlId = mindao.getDeclaredMethod("setAge", int.class);
        setControlId.invoke(o, 10);
        System.out.println(mindao.getDeclaredMethod("getAge").invoke(o));
    }
}

运行main():
1.当前项目中没有User.class

10
com.moon.MyTestClassLoad@74a14482
--------------------------
com.moon.MyTestClassLoad@74a14482
20

Process finished with exit code 0

2.该项目中存在User.class 会使用自定义加载器父类进行委托加载

20
sun.misc.Launcher$AppClassLoader@18b4aac2
--------------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
20

如何破坏双亲委派:

在 MyTestClassLoad 中 重写 loadClass();不交给父类进行委派加载即可:

 @Override
    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();
                    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;
        }
    }

2个同包同类名的class从理论上是可以在一个项目中存在的,判断唯一除了判断同包,同路径还有看是否是同一个类加载器。例如新建一个项目和现在项目中创建一个同包同类名的类,,一个用自定义class进行加载,一个用默认的class进行加载。或者用自己写的打破双亲委派机制 就能加载 不同路径下的通包同类名的class ,如下图实例创建2个UserClass ,一个age设置就等于本身,user2目录下getAge 方法比为 age+10进行加载:
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值