类加载器详解

 

package com.yang.jvm;

import sun.net.spi.nameservice.dns.DNSNameService;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println(System.getProperty("sun.boot.class.path")); //bootStrap启动类的加载路径
        System.out.println(System.getProperty("java.ext.dirs")); //拓展类的加载路径
        System.out.println(System.getProperty("java.class.path"));  //应用类或者说是系统类的加载路径
        //因为字节码是使用类加载器加载的,所以每个字节码都会记录加载该字节码的类加载器,所以就可以通过字节码的getClassLoader()获得类加载器
        System.out.println(String.class.getClassLoader());//启动类加载器使用的是C或C++语言写的,所以打印启动类加载器都是返回null,
        System.out.println(DNSNameService.class.getClassLoader());//输出的是拓展类加载器sun.misc.Launcher$ExtClassLoader@2f333739
        System.out.println(Test.class.getClassLoader());//应用类加载器sun.misc.Launcher$AppClassLoader@14dad5dc


    }
}

1.类加载器机制:

 

 2. 通过源码看内置类加载器的创建过程,也就是Launcher类,除了启动类加载器是c++写的,ext和app类加载器是java写的:

 3.源码查询双亲委派机制:通过app类加载器的load方法

 为什么要设计双亲委派机制?

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

 

 

 4. 打破双亲委派机制:应用场景:tomcat在部署不同的war包时,每个war包可能拥有相同的类,但版本不同,如war1的spring是4.0版本 war2的版本是5.0版本,如果没打破双亲委派机制,那么

war1如果先加载,委派app类加载器加载了spring4.0的jar包,war2去加载时,委托app类加载器,此时spring4.0已存在,那么spring5.0就不会被加载了

 

 5.类加载器的隔离性:在一个jvm进程中,同个类可以被不同的类加载器加载,虽然他们的包名是一致的,下面自定义类加载器来说明这一点:

 a.在2个不同的文件夹下分别放入相同的类F

 

 

 

 

 

 

 b.自定义类加载器,并打破双亲委派机制

 

public class MyClassLoader extends ClassLoader {

    public   String path="";

    public MyClassLoader(String path) {
        super();
        this.path=path;
    }

    @Override
    protected Class<?> findClass(String name2) throws ClassNotFoundException {
        int i = name2.lastIndexOf(".")+1;
        String className=name2.substring(i);

        String name=name2.substring(0,i).replace(".","\\")+"\\"+className+".class";
        try {
            FileInputStream fileInputStream = new FileInputStream(new File(path + name));
            byte[] data=new byte[fileInputStream.available()];
            fileInputStream.read(data);
            Class<?> aClass = super.defineClass(name2, data, 0, data.length);
            return aClass;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        //如果是我们自己定义的类,就不委派给父加载器 也就是打破双亲委派机制
        if(name.startsWith("com.yang")){  
            return this.findClass(name);
        }
        //其他类的加载委派给父加载器
        return super.loadClass(name);
    }


}

执行测试代码:

 6. 类加载的过程

 

 验证:将class文件读取后,需要校验格式对不对,如class文件的开头都是cafe baby这几个字母

 准备:给静态变量赋默认值,如bolean的默认值是flase

 解析:分静态连接和动态连接,类的加载阶段指是的是静态连接,连接是指将符号引用转成直接引用,啥意思:如 main()方法,方法名是一个字符串,

 它加载到内存中,至少要有个地方存储它的信息吧,存储就会有个地址,因此这里就是将main()方法这个符号指向内存中的地址

 初始化:给静态变量赋真正的值,执行init()方法

 7.类的加载是懒加载,也就是用到才会加载:

.

public class F {
  static {
      System.out.println("我是F我被加载了");
  }
}
public class F2 {
    static {
        System.out.println("我是F2我被加载了");
    }
}
public class KK {

    public static void main(String[] args) {
        F f = new F();
        F2 f2=null; //不会被加载

    }
}

 

 8. 全盘负责委托机制:加载一个A类的类加载器,同时也会使用给类加载器加载A类引用的相关类,除非这些类已经明确使用某个类加载器来加载

9. 类加载器和它的父加载器的关系是组合关系而不是继承关系

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-09-25 10:22  yangxiaohui227  阅读(194)  评论(0编辑  收藏  举报