目录
一.类加载器
1.作用:加载class文件
举例
2.过程详解
代码示例
3.类加载器的种类
①启动类(根)加载器(Bootstrap ClassLoader,爷爷)
②扩展类加载器(Extension ClassLoader,爸爸)
③应用程序加载器(Application ClassLoader,儿子)
举例:
二.双亲委派机制
1.目的:为了保证 Java 核心类库的安全,防止它们被恶意篡改
2.“双亲委派”的含义
3.代码举例:故意篡改String类,看看是否生效
4.代码举例:我们自定义一个Student类,肯定就随便了
一.类加载器
1.作用:加载class文件
举例
new Student();
这在做什么事情?
创建了一个Student类的具体实例(对象),引用(变量名)放在了栈,而具体的对象放在了堆。
2.过程详解
类加载器,负责下图的Car.class这个类文件,加载成Car这个类(然后就可以根据这个类,创建具体的实例了),过程如下:
代码示例
//类是模板,对象是实例 Car car1 = new Car(); Car car2 = new Car(); System.out.println(car1 == car2); //获取这两个对象的类模版 Class<? extends Car> class1 = car1.getClass(); Class<? extends Car> class2 = car2.getClass(); System.out.println(class1 == class2);运行结果:
可见两个对象的类模板是同一个。(这也是一句废话,因为创建的时候就是根据同一个类new的实例,再倒回去肯定也是同一个类)
3.类加载器的种类
类加载器,其实也不像大家想的那么简单,它其实也分成很多种类。而且他们之间有层级关系,如下图:
①启动类(根)加载器(Bootstrap ClassLoader,爷爷)
②扩展类加载器(Extension ClassLoader,爸爸)
③应用程序加载器(Application ClassLoader,儿子)
举例:
//类是模板,对象是实例 Car car1 = new Car(); //获取这个对象的类模版 Class<? extends Car> class1 = car1.getClass(); System.out.println("儿子:"+class1.getClassLoader()); System.out.println("爸爸:"+class1.getClassLoader().getParent()); System.out.println("爷爷:"+class1.getClassLoader().getParent().getParent());运行结果:
解读:
- 可见此时儿子就是“AppClassLoader”,即:应用程序加载器
- 父亲是“PlatformClassLoader”(我用的JDK17,给这个加载器改名了,JDK8叫ExtClassLoader),即:扩展类加载器
- 爷爷是null,即:启动类加载器(因为启动类加载器它是用 C++ 写的,不是 Java 写的,故获取不到,因此用null代表它)
二.双亲委派机制
1.目的:为了保证Java 核心类库的安全,防止它们被恶意篡改
2.“双亲委派”的含义
①先声明,这是翻译时候出了错误,正确的情况下应该叫“父亲委派”。
②双亲委派机制的过程描述:
当一个类加载器收到类的加载请求时,它不会自己动手,而是先将请求向上委派给它的父加载器,这个委派过程会一直持续到最顶层的启动类加载器;只有当父加载器在自己的加载范围内找不到这个类时,请求才会原路返回,由下一级加载器尝试自己加载,如果所有加载器都找不到,最终才会抛出异常。
3.代码举例:故意篡改String类,看看是否生效
如下图,我们自己伪造了一个String类,并在该类中定义了一个main方法。
但是运行以后,发现程序报错“在java.lang.String中找不到main方法”。
思考:我们自己明明定义了main方法,为什么偏偏报错说找不到呢?
答案:
①从人情角度讲,String可是Java官方核心类库里面的东西,最权威了,岂是你说改就改的?所以肯定是不允洗你瞎改的,这很合理。
②从“双亲委派机制”的角度讲一下原理:
- JVM 需要加载
java.lang.String这个类,但“应用程序加载器”自己先不加载,而是不断向上交给父加载器去处理(双亲委派机制)。- 根据双亲委派,加载的请求最终一步步交给了启动类加载器(Bootstrap ClassLoader,是最顶层的类加载器)。
- 启动类加载器在
rt.jar(JDK 核心库)里找到了官方的String类,于是会进行加载操作。- 结果:JVM 加载并使用的是官方的 String,而不是你写的这个,而人家官方的String类里面可是没有main方法的,因此就会报错找不到。
综上,可体现出,双亲委派机制本质上就是为了构建一道安全防线,确保 Java 的核心类库不被随意篡改。说白了就是,当你想篡改核心类库的东西的时候,你的应用程序加载器等级太低,不配先加载你自定义的假官方类,而是要遵循“双亲委派机制”,向上一步步交给“启动类加载器”先加载,而启动类加载器里面就有核心类库的所有类,因此会加载。而你折腾半天,人家理都不理,直接越过了。可见这个机制确实安全。
4.代码举例:我们自定义一个Student类,肯定就随便了
因为Student类是啥东西?我们自定义的,也不属于Java核心类库的东西,人家也用不着保护。
所以应用程序加载器根据双亲委派机制,一步步上报到启动类加载器时,人家没有,然后往回退,扩展类加载器也没有该类,结果最终回退到应用程序加载器,于是我们自己写的Student类就被加载了。
本质原因就是自定义的Student类,不属于核心类库,才让应用程序加载器有机会加载,不然会被它的父加载器拦截。
以上就是本篇文章的全部内容,喜欢的话可以留个免费的关注呦~~~