ClassLoad

2022-05-13 11:00:17 浏览数 (1)

类从编译到执行经历的的过程

➢编译器将Robot.java源文件编译为Robot.class字节码文件 ➢ClassLoader将字节码转换为JVM中的Class <Robot>对象 ➢JVM利用Class <Robot>对象实例化为Robot对象

ClassLoader

Classloader在Java中有着非常重要的作用 它主要工作在Class装载的加载阶段,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流或者是从系统外部获得Class二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。

ClassLoader的种类
  • BootStrapClassLoader:C 编写,java的核心自带类库的java.* (用户不可见除非看JVM代码)
  • ExtClassLoader:Java编写,扩展库javax.*
  • AppClassLoader:Java编写,加载程序所在目录主要加载我们的src程序代码
  • 自定义ClassLoader:Java编写,定制化加载
ClassLoader的源码和解析过程分析:

ClassLoader里面包含了许多方法制定了加载流程和方式

其中loadClass是比较重要的,可以根据类名查找当前ClassLoader是否加载了该class,如果有的话就返回类的class实例,找不到就会返回null

手写classloader关键

代码语言:javascript复制
手写ClassLoader
package jvm;

import java.io.*;

public class MyClassLoader extends ClassLoader{
    private String path;
    private String MyClassLoaderName;//定义自己的ClassLoader名字 实际上没啥用

    public MyClassLoader(String path, String MyClassLoaderName) {
        this.path = path;
        this.MyClassLoaderName = MyClassLoaderName;
    }
    @Override
    //用于寻找类文件
    public Class findClass(String name){
        byte[] b=loadClassData(name);
        return  defineClass(name,b,0,b.length);
    }

    //用于加载class文件为字节码
    private byte[] loadClassData(String name) {
        String url=path name ".class";
        InputStream inp=null;
        ByteArrayOutputStream out=null;
        try {
            inp=new FileInputStream(new File(url));
            out=new ByteArrayOutputStream();
            int i=0;
            while ((i=inp.read())!=-1){
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
           try{
               out.close();
               inp.close();
           }catch (Exception e)
           {
               e.printStackTrace();
           }
        }
        return out.toByteArray();
    }

}
代码语言:javascript复制
package jvm;

public class ClassLoaderCheck {
    public static void main(String[] args) throws Exception {
        MyClassLoader myl=new MyClassLoader("C:\Users\Zyh\Desktop\","ZyhClassLoader");//我的桌面C:\Users\Zyh\Desktop\上有个ClassTest.class文件,ZyhClassLoader没有啥左右就是给我的classloader起个名字而已
        Class c = myl.loadClass("ClassTest");
        System.out.println(c.getClassLoader());
        c.newInstance();
    }
}
为什么我们要手写ClassLoader,有什么好处呢?

可以加载任意字节流,比如访问远程网络加载二进制流并生成我们需要的类 我们也可以对某些敏感的class进行加密,自定义findClass()进行解密处理

ClassLoader的双亲委派机制
双亲委派模型的工作过程是:
  • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。
  • 每一个层次的类加载器都是如此。因此,所有的加载请求最终都应该传送到顶层的启
  • 只有当父加载器反馈自己无法完成这个加载请求时(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。
为什么要使用双亲委派机制去加载类
  • 避免一个class被多次装载
  • 父类加载器已经加载过的类,不用再次加载,而且对于一些系统类,用户自定义的不起作用了,有一定安全保证。
类加载方式

隐式加载:new 显式加载:loadClass,forName等

类加载过程
loadClass和forName的区别
  • Class.forName得到的class是已经初始化完成的
  • Classloder.loadClass得到的class是还没有链接的

举例子:

forName是执行了初始化的有时候我们需要得到已经初始化的class比如注册mysql驱动

有时候我们为了加快初始化速度会使用lazy-load(比如springIOC读取bean的配置文件),而使用ClassLoader不用进行链接和初始化省去执行static方法等的时间花费,加快加载速度,等需要时候再进行使用

0 人点赞