类加载器ClassLoader

class装载验证流程

加载,链接(验证,准备,解析),初始化

一,加载:首先取得二进制流(网络,文件等等),然后转为方法区的数据结构,并在堆中生成对应的java.lang.Class对象

二,链接:
1.验证(保证文件格式,等)格式的正确性

2.准备 分配内存,并为类设置初始值(方法区中)
方法区-(类信息,常量池,静态字段,方法)加载进内存以后

敲黑板~

在准备阶段会为方法区属性设置初始值(为静态变量)
例如public static int i = 1;此时准备阶段 i值为0;
当初始化阶段中会被设置为1;
当静态变量使用final修饰时,那么准备阶段就会被附上正确的值。
public static final int i = 1;准备阶段 i值为1;

3.解析 符号引用替换为直接引用
符号引用相当于在 常量池里头的"java.lang.Object";
直接引用指针或地址偏移量,引用对象一定存在内存。

三,初始化
初始化阶段,会执行类的构造器
会为static变量正确的值,会执行static{}中的语句
并且是线程安全的,执行时其他线程暂停。

java.lang.NoSuchFieldError错误在什么阶段抛出?
首先,加载阶段是不会的,它只是读取字节流
我认为应该在链接的认证阶段,当发现(存取或改变数据域所产生的错误)就抛出了。

出现了 NoSuchMethodError 或者 NoSuchFieldError ,这时一般是应用的classpath下包含了多个包含了想同类的jar包,而很不幸的加载到了 不正确 的jar包。

ClassLoader

它是一个抽象类,它的实例把JAVA字节码加载入JVM虚拟机,只负责了类装载过程的加载阶段。

ClassLoader的一些方法:
public Class<?> loadClass(String name) throws ClassNotFoundException
载入并返回一个Class对象

protected final Class<?> defineClass(byte[] b, int off, int len)
定义一个类,不公开调用,参数: class字节码,位置等

protected Class<?> findClass(String name) throws ClassNotFoundException
loadClass回调该方法,自定义ClassLoader的推荐做法 ,寻找class

protected final Class<?> findLoadedClass(String name)
寻找已经加载的类

基于双亲委派模型,自顶向上检查类是否已经加载,自顶向下尝试加载类。
ClassLoder的默认设计模式

在本加载器中先寻找,没找到,查看parent是否有,有的话让父加载器尝试加载。

强制在App ClassLoader中加载,(前提是:-Xbootclasspath/a:D:/tmp/clz 这个类在此目录 bootClassLoader会加载这个类)

public static void main(String args[]) throws Exception {
    ClassLoader cl=FindClassOrder2.class.getClassLoader();
    byte[] bHelloLoader=loadClassBytes("geym.jvm.ch6.findorder.HelloLoader");
    Method md_defineClass=ClassLoader.class.getDeclaredMethod("defineClass", byte[].class,int.class,int.class);//得到App ClassLoder的 defineClass方法
    md_defineClass.setAccessible(true);//原本是受保护的
    md_defineClass.invoke(cl,bHelloLoader,0,bHelloLoader.length);//执行defineClass方法
    md_defineClass.setAccessible(false);

    HelloLoader loader = new HelloLoader();
    System.out.println(loader.getClass().getClassLoader());
    loader.print();
}
//这样就会在App ClassLoader中加载

能否只用反射,将类注入到bootClassLoader?
我觉得应该不可以,虽然知道获取到当前appClassLoader的父类的父类即可。但boot是null。没办法的。

双亲委派模式的缺点?
顶层的ClassLoader无法加载底层的ClassLoader。底层加载的类,顶层不能获取。

javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。

这时如何调用呢?如何让顶层访问底层呢?
使用上下文加载器。
Thread.setContextClassLoader();
这只是一个角色,任何加载器都能扮演这个角色(上下文加载器)。
这样,在顶层的ClassLoader中可以传入底层的ClassLoader。

static private Class getProviderClass(String className, ClassLoader cl,
        boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
    try {
        if (cl == null) {
            if (useBSClsLoader) {
                return Class.forName(className, true, FactoryFinder.class.getClassLoader());
            } else {
                cl = ss.getContextClassLoader();
                if (cl == null) {
                    throw new ClassNotFoundException();
                }
                else {
                //-------------------------注意
                    return cl.loadClass(className); //使用上下文ClassLoader  cl就是
                    //--------------------------
                }
            }
        }
        else {
            return cl.loadClass(className);
        }
    }
    catch (ClassNotFoundException e1) {
        if (doFallback) {
            // Use current class loader - should always be bootstrap CL
            return Class.forName(className, true, FactoryFinder.class.getClassLoader());
        }

Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent

OSGi的ClassLoader形成网状结构,根据需要自由加载Class

例子:OrderClassLoader的部分实先

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    // First, check if the class has already been loaded
    //找,并加载
    Class re=findClass(name);
    if(re==null){
        System.out.println(“无法载入类:”+name+“ 需要请求父加载器");
        return super.loadClass(name,resolve);
    }
    return re;
}
protected Class<?> findClass(String className) throws ClassNotFoundException {
//寻找····················
Class clazz = this.findLoadedClass(className);
if (null == clazz) {
    try {
        String classFile = getClassFile(className);
        FileInputStream fis = new FileInputStream(classFile);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel outC = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
         省略部分代码
        fis.close();
        byte[] bytes = baos.toByteArray();
        //加载·························
        clazz = defineClass(className, bytes, 0, bytes.length);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
return clazz;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值