中:第4章 再谈类的加载器

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


01-概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1-大厂面试题

在这里插入图片描述
在这里插入图片描述

2-类加载器的分类

在这里插入图片描述

public class UserTest {
    public static void main(String[] args) {
        User user = new User(); //隐式加载

        try {
            Class clazz = Class.forName("com.atguigu.java.User"); //显式加载
            ClassLoader.getSystemClassLoader().loadClass("com.atguigu.java.User");//显式加载
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

3-类加载器的必要性

在这里插入图片描述

4-命名空间

在这里插入图片描述
代码解释:
在这里插入图片描述
结果:
在这里插入图片描述

5-类加载机制的基本特征

在这里插入图片描述

6-类加载器之间的关系

Launcher.java类:
public class Launcher {
    ……

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        Thread.currentThread().setContextClassLoader(this.loader);
                ……
}
分析:
1、验证扩展类加载器的父类是null
先看:
 var1 = Launcher.ExtClassLoader.getExtClassLoader();
获取到扩展类加载器,点击该方法往里面追溯,在找到:
return new Launcher.ExtClassLoader(var0);
我们在点击该方法往里面追溯,在找到:
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
然后点击super,往里面追溯,在找到:
public URLClassLoader(URL[] urls, ClassLoader parent,
                      URLStreamHandlerFactory factory) {
    super(parent);
点击其中的parent就是null,我们点击super,往里面追溯,在找到:
protected SecureClassLoader(ClassLoader parent) {
    super(parent);
点击其中的parent就是null,我们点击super,往里面追溯,在找到:
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
点击其中的parent就是null,我们点击this,往里面追溯,在找到:
private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
由于parent就是null,所以扩展类加载器的父类是null,也就是引导类加载器,因此我们调用获取扩展类加载器父类的方法获得的结果是null
 2、验证系统类加载器的父类是扩展类加载器
先看:
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
获取到系统类加载器,点击该方法往里面追溯,在找到:
return new Launcher.AppClassLoader(var1x, var0); 
其中var0就是扩展类加载器,点击AppClassLoader,往里面追溯,在找到:
AppClassLoader(URL[] var1, ClassLoader var2) {
    super(var1, var2, Launcher.factory);
    this.ucp.initLookupCache(this);
}
其中var2就是扩展类加载器,我们点击super,往里面追溯,在找到:
public URLClassLoader(URL[] urls, ClassLoader parent,
                      URLStreamHandlerFactory factory) {
    super(parent);
里面的parent就是扩展类加载器,我们点击super,往里面追溯,在找到:
protected SecureClassLoader(ClassLoader parent) {
    super(parent);
里面的parent就是扩展类加载器,我们点击super,往里面追溯,在找到:
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
里面的parent就是扩展类加载器,我们点击this,往里面追溯,在找到:
private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
由于parent就是扩展类加载器,所以系统类加载器的父类是扩展类加载器,因此我们调用获取系统类加载器父类的方法获得的结果是扩展类加载器
3、当前线程上下文的ClassLoader就是系统类加载器
Thread.currentThread().setContextClassLoader(this.loader)就是将系统类加载器设置为当前线程的上下文加载器,所以Thread.currentThread().getContextClassLoader()获取到的就是系统类加载器

02-复习:类的加载器分类

在这里插入图片描述
在这里插入图片描述
父类加载器和子类加载器的关系:
在这里插入图片描述

1-引导类加载器

在这里插入图片描述
在这里插入图片描述
引导类加载器需要加载的jar包文件:
在这里插入图片描述
执行结果:
在这里插入图片描述

2-扩展类加载器

在这里插入图片描述
无法通过扩展类加载器获得引导类加载器,因为引导类加载器是用C/C++语言编写的,所以获取的值是null
扩展类加载器:
在这里插入图片描述
执行结果:
在这里插入图片描述

3-系统类加载器

在这里插入图片描述

4-用户自定义类加载器

在这里插入图片描述

03-测试不同的类的加载器

在这里插入图片描述

在这里插入图片描述
获取当前线程上下文的ClassLoader的结果就是系统类加载器,这个可以在Launcher.java中被代码证明,即:this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);Thread.currentThread().setContextClassLoader(this.loader);
代码:

public class ClassLoaderTest1 {
    public static void main(String[] args) {
        //获取系统该类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //获取扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d
        //试图获取引导类加载器:失败
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);//null

        //###########################
        try {
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println(classLoader);
            //自定义的类默认使用系统类加载器
            ClassLoader classLoader1 = Class.forName("com.atguigu.java.ClassLoaderTest1").getClassLoader();
            System.out.println(classLoader1);

            //关于数组类型的加载:使用的类的加载器与数组元素的类的加载器相同
            String[] arrStr = new String[10];
            System.out.println(arrStr.getClass().getClassLoader());//null:表示使用的是引导类加载器

            ClassLoaderTest1[] arr1 = new ClassLoaderTest1[10];
            System.out.println(arr1.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2

            int[] arr2 = new int[10];
            System.out.println(arr2.getClass().getClassLoader());//null:不需要类的加载器


            System.out.println(Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

04-ClassLoader源码解析

在这里插入图片描述

ClassLoader的主要方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

loadClass()剖析

loadClass()方法是ClassLoader.java类中的主要方法。

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

在这里插入图片描述
在这里插入图片描述
分析:
假设现在需要加载User类,我们自然也知道这需要使用系统类加载器加载,接下来来到系统类加载器的loadClass()方法中,假设系统类加载器没有加载User类,同步代码块的作用在上面注释中写的很清楚,然后直接获取User类的Class对象,如果c为null,将会判断系统类加载器的父类加载器是否为空,我们知道系统类加载器的父类加载器是扩展类加载器(在Launcher.java类中验证),那么parent不为null,之后进入if判断,将会调用扩展类加载器的loadClass()方法,此方法和上面的loadClasss()方法是一样的,接下来来到扩展类加载器的loadClass()方法中的判断就不在说了,因此扩展类加载器没有加载User类,所以c是null,然后parent是null(在Launcher.java中验证,通过扩展类加载器获取到的父类加载器就是null),将会执行c=findBootstrapClassOrNull(Name),这个就是判断引导类加载器是否加载了User类,如果没有加载该类就会尝试加载User类,也就是如下代码:
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;

return findBootstrapClass(name);

}
其中checkName()方法不用管,在本例中那个不会执行,然后会执行findBottstrapClass()fhfa ,如果加载成功返回对应的Class实例,否则返回null,由于引导类加载器不会加载User类,所以本次结果肯定是null了,那回到扩展类加载器的loadClass()方法中,继续看c = findBootstrapClassOrNull(name),那c就是null了,之后便会调用c = findClass(name);,这个将会调用URLClassLoader类中的重写findClass方法,这个方法就不带大家看了,不过该方法会返回一个null值,毕竟User类不是被扩展类加载器加载的,接下来回到系统类加载器的loadClass()方法中,继续看c = parent.loadClass(name, false),由于返回值c是null,然后便会调用c = findClass(name),系统类加载器正好可以加载User类,返回一个Class对象

SecureClassLoader与URLClassLoader

在这里插入图片描述
在这里插入图片描述

ExtClassLoader与AppClassLoader

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Class.forName()与ClassLoader.loadClass()

在这里插入图片描述

05-双亲委派模型

定义与本质

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

优势与劣势

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

破坏双亲委派机制

破坏双亲委派机制1

在这里插入图片描述
以上简单来说就是jdk1.2之前还没有引入双亲委派机制,所以jdk1.2之前就是破坏双亲委派机制的情况

破坏双亲委派机制2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
简单来说就是线程上下文类加载器让启动类加载器和系统类加载器直接联系起来了,中间的扩展类加载器被省略了,所以这破坏了双亲委派机制,其中线程上下文类加载器就是系统类加载器,这个证明在01-概述—>载器之间的关系中有解释

破坏双亲委派机制3

在这里插入图片描述
在这里插入图片描述

热替换的实现

在这里插入图片描述
在这里插入图片描述
每次调用方法之前都要加载字节码文件,然后创建对象,我们可以把字节码文件变成最新的,那么创建的对象肯定是最新的,所以这就完成了热替换

06-沙箱安全机制

在这里插入图片描述

JDK1.0时期

在这里插入图片描述

JDK1.1时期

在这里插入图片描述

JDK1.2时期

在这里插入图片描述
在这里插入图片描述

JDK1.6时期

在这里插入图片描述

07-自定义类的加载器

在这里插入图片描述
在这里插入图片描述

实现方式

在这里插入图片描述
在这里插入图片描述

08-Java9新特性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码:

public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(ClassLoaderTest.class.getClassLoader());
        System.out.println(ClassLoaderTest.class.getClassLoader().getParent());
        System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getParent());
 
        //获取系统类加载器
        System.out.println(ClassLoader.getSystemClassLoader());
        //获取平台类加载器
        System.out.println(ClassLoader.getPlatformClassLoader());
        //获取类的加载器的名称
        System.out.println(ClassLoaderTest.class.getClassLoader().getName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值