JVM系列(二)[Class加载过程,双亲委派机制,自定义一个ClassLoader,LazyLoading,父子类构造器加载顺序,混合型-解释&编译]

类加载和初始化

class cycle

首先一个class文件在硬盘里面
然后JVM去对它进行以下行为:

  1. Loading,把class文件load到内存,双亲委派(安全)
  2. Linking,分三小步:
    1. verification,校验文件是否复合JVM规定的class格式.
    2. preparation,静态变量赋默认值,比如int-0,double-0.0,boolean-false
    3. resolution,解析,
      将类、方法、属性等符号引用解析为直接引用
      如常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
  3. Initializing,初始化,这时候静态变量赋初始值(代码指定的值),调用静态代码块

一般我们只要记住,静态变量的初始化分为两步,Linking时是默认值,Initializing后才是初始化的值
成员变量的初始化其实也是分两步,第一步申请内存空间时是默认值,第二步调用构造方法时才是初始化的值

在这里插入图片描述

类加载器ClassLoader

一个class文件的Loading,load出两个东西:
1.class文件的二进制编码加载到内存
2.生成一个与之对应的Class对象,改对象指向内存中的class编码文件

如果打印一下String的ClassLoader.会发现结果为null:
System.out.println(String.class.getClassLoader());

这是因为:
最顶层的加载器Bootstrap是用C++来实现的,在JAVA中没有与之对应的类.
所以Bootstrap加载出来的类,比如String,获取到的ClassLoader是null.

类加载器的分层关系:
在这里插入图片描述

注意
1.这个上层加载器,即父加载器,是逻辑上的关系,其实就是一个成员变量
2.不是类的继承关系,那是另一种维度的关系
3.加载器也是一个对象,也要由另一个加载器加载,但并不一定是由他的parent加载,是谁不一定.最终都是由Bootstrap加载的
设一个加载器a的上层加载器是b,那么 a不一定是被b加载的

求证一下BootStrap,Ext,App都加载哪些类

其实AppClassLoader和ExtClassLoader都是sun.misc.Launcher的内部类
(JDK11把Launcher换成了ClassLoaders)
Launcher里面可以看到这三个内部类,和他们各自加载的path
BootClassPathHolder:System.getProperty(“sun.boot.class.path”)
AppClassLoader:java.class.path
ExtClassLoader:java.ext.dirs

我们写个方法打印一下这些东西就看出来了:

	public static void main(String[] args) {
   
        System.out.println("boot-------------------");
        System.out.println(System.getProperty("sun.boot.class.path").replaceAll(":", System.lineSeparator()));
        System.out.println("ext-------------------");
        System.out.println(System.getProperty("java.ext.dirs").replaceAll(":", System.lineSeparator()));
        System.out.println("classpath-------------------");
        System.out.println(System.getProperty("java.class.path").replaceAll(":", System.lineSeparator()));
    }

双亲委派

是什么

双亲委派并不是指父母双方,而是指"查找类时从子到父,加载类时从父到子"的这么一个机制.

具体含义:
众所周知,ClassLoader加载完一个类后,会放入一个ClassCache,下次再用时就不需重复加载了.每个ClassLoader有自己的ClassCache

  • 当我们需要找一个类时,会先交给最下层的ClassLoader,在ClassCache找,如果找到了就返回结果,如果找不到就交给上层加载器,上层加载器进行同样的操作,直到Bootstrap.
  • 真正去加载这个类的时候,会自上到下开始加载.
    每个ClassLoader先看自己管辖的类里面有没有需要加载的class,如果有就加载返回,如果没有就交给下一层去加载.
    如果都没有就抛异常CLassNotFoundException.

为什么

为啥不直接放到一个ClassCache里面,这样就不用层层查找了啊?
这里主要是出于安全考虑.
假设黑客小明自定义了一个java.lang.String对象,里面做了些非法操作;如果不分层查找的话,用户就会用到他自定义的String,阴谋得逞;
双亲委任机制下,使用String时,先看看父加载器是否已加载,直到找到Bootstrap后直接返回String类.

可以打破双亲委派机制吗?

可以,自定义一个classLoader,重写loadClass方法就可以打破.
热加载/热部署的时候,可能会重写loadClass(),打破双亲委派机制

从源码角度去理解ClassLoader

  1. 继承ClassLoader(这里用到了模板方法设计模式)
  2. 重写模板方法findClass,调用defineClass
  3. 自定义类加载器 加载 加密的class,防止反编译和篡改

ClassLoader简单读源码

别的方法ClassLoader类已经写好啦(模板设计模式),
关键点就是下面的findClass方法,该方法直接抛异常,是个必须被子类重写的方法.(模板方法,钩子函数)

	protected Class<?> loadClass(String name, 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值