深刻分析ClassLoader

首先介绍下ClassLoader:
ClassLoader顾名思义就是类加载器,负责将Class加载到JVM中,事实上ClassLoader除了能将Class加载到JVM中以外,还有一个重要的做用就是审查每一个类应该有谁加载,ClassLoader是一个父优先的等级加载机制。ClassLoader除了上述两个做用外还有一个任务就是将Class字节码从新解析成JVM统一要求的对象格式-----------由此本文能够划分红三点java

  1. ClassLoader类结构分析
    1. ClassLoader经常使用的方法
    输入图片说明
    1,defineClass():用来将byte字节流解析成JVM可以识别的Class对象
    2,findClass():用来子类扩展的方法,目的是为了寻找类
    3,loadClass():加载类,这里就能够动态加载了,程序运行的时候加载
    4,resolveClass():这个方法是用来连接这个类
    JDK给咱们提供了能够扩展的,也就是咱们能够自定义的类加载器URLCLassLoader,比较方便app

    2. ClassLoader的等级加载机制

    输入图片说明

    1,BootStrap ClassLoader:
    这是一个引导类加载器,首先声明一下虽然说在类的结构中能够看到这个类,可是这个类只有Class文件,据了解这个类是经过C++编写的,这也就是ExtClassLoader的父类为空的缘由。引导类负责加载JDK的jre下的类库,例如rt.jarjsp

    2,ExtClassLoader:
    这是一个扩展类加载器,加载bin目录下的ext文件夹下的jar包,很少说ide

    3,AppClassLoader:
    这个类加载器就是咱们自定义的类,例如咱们的这个方法getSystemClassLoader()做为父加载器,这个加载器也就是AppClassLoader。测试

    4,加载类过程总结:
    看了不少资料都是什么双亲委托加载机制,具体我也不是很清楚,能够我太low,可是我我的的理解就是,当加载一个类的时候:首先检查这个类是否已经被本身加载过,若是已经加载过,就拒绝本次加载,若是没有加载过,就会抛给他的父类,而后父类在检查是否加载,而后在抛给父类,直到所有检查完都没有发现这个类被加载,那么就有意思了,由于每个类加载器都有本身的加载范围,而后开始判断,父类不加载类,那么就开始向下寻找这个类的加载器,而后这个类被加载,也就是先向上检查是否加载,其次向下看是否能够加载!--------------------这里有一个讲解,来帮助理解,若是咱们本身写了一个java.lang.String,那么类加载器就先去加载jdk下的java.lang.String.遇到咱们本身定义的类就不会加载了,这也就是java.lang.Object首先被加载,父类加载会防止咱们破坏干扰jre的正常运行,致使类结构被破坏this

这里是类关系的代码spa

public class ClassA {
}
public class ClassB {
}
public class ClassTest {
    public static void main(String[] args) {
        //演示 我們的類加載器
        System.out.println("ClassTest-------------ClassLoader:"+ClassTest.class.getClassLoader());
        System.out.println("ClassTest.Parent------ClassLoader:"+ClassTest.class.getClassLoader().getParent());
        System.out.println("ClassTest.GrandFather-ClassLoader:"+ClassTest.class.getClassLoader().getParent().getParent());
        
        //用户自定义的类加载   都是经过AppClassLoader加载
        System.out.println(ClassA.class.getClassLoader());
        System.out.println(ClassB.class.getClassLoader());
        //结论就是----------当两个类名称不一样,那么这两个类就不相同
    }
}

打印结果3d

ClassTest-------------ClassLoader:sun.misc.Launcher$AppClassLoader@73d16e93
ClassTest.Parent------ClassLoader:sun.misc.Launcher$ExtClassLoader@15db9742
ClassTest.GrandFather-ClassLoader:null
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$AppClassLoader@73d16e93
  1. 如何判断两个类相同
  • 类名称不一样
这里就不须要介绍了,只要类名不一样,那么类确定也不一样
  • 包名称不一样
package me.classloader.classpackage;

public class String {

}


package me.classloader.classpackage;

import org.junit.Test;

public class ClassTest {
    @Test
    public void classTest(){
        //这里是  BootStrap ClassLoader-------所以返回Null
        System.out.println(java.lang.String.class+"------classLoader:"+java.lang.String.class.getClassLoader());
        //这里 咱们自定义的类        被AppClassLoader 加载
        System.out.println(String.class+"--------ClassLoader:"+String.class.getClassLoader());
        //很明显这两个类是不一样的
    }
}
这里能够获得咱们的包名能够引发 两个类不是同一个类,而且,这里JVM就近调用一个类先调用的同包下的类
  • 类加载器不一样
package me.classloader.classloader;

public class Demo {
    private Demo demo;

    public void setDemo(Demo demo) {
        this.demo = demo;
    }
    
}

package me.classloader.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

//本身写的ClassLoader
public class MyClassLoader extends ClassLoader {

    private String classPath;// 类路径

    public MyClassLoader() {
    }

    public MyClassLoader(String classPath) {

        this.classPath = classPath;
    }

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

        byte[] classDate = getDate(name);
        if (classDate == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classDate, 0, classDate.length);
        }
    }

    // 将 class 文件转化成字节流
        //className是类名称---权限定名
    private byte[] getDate(String className) {
        // 将XX.XX.java --------->XX/XX/java.class
        String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int num = 0;
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0, num);
            }
            return stream.toByteArray();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

package me.classloader.classloader;

public class ClassTest {
    public static void main(String[] args) throws Exception{
        String classPath ="E:\\javaprogram\\java-exercise\\classload\\bin";
        String className = "me.classloader.classloader.Demo";
        ClassLoader appClassLoader = ClassTest.class.getClassLoader();
        MyClassLoader myClassLoader = new MyClassLoader(classPath);
        Class demo1 = appClassLoader.loadClass(className);
        System.out.println(demo1.newInstance().getClass().getClassLoader());
        Class demo2 = myClassLoader.findClass(className);
        System.out.println(demo2.newInstance().getClass().getClassLoader());
        System.out.println(demo1.newInstance().getClass() ==demo2.newInstance().getClass());
    }
}
测试结果
sun.misc.Launcher$AppClassLoader@73d16e93
me.classloader.classloader.MyClassLoader@6d06d69c
false

经过上面的代码,咱们也知道了如何写一个类加载器,其实查看findClass()是ClassLoader提供的一个模板方法!代理

总结:类加载器也是一个比较深的东西,例如servlet中jsp使用了热部署!其实就是类加载器的应用,可能动态代理也用到了类加载器,由于实际工做加载的是咱们代理的类code

相关文章
相关标签/搜索