2.双亲委派机制详细解析及原理 (6)

再看看这四个步骤:

private Class<?> defineClass(String name, Resource res) throws IOException { long t0 = System.nanoTime(); int i = name.lastIndexOf(\'.\'); // 获取classes目录的绝对路径,如:file:/Users/用户名/workspace/demo/target/classes/ URL url = res.getCodeSourceURL(); if (i != -1) { // 获取包名 String pkgname = name.substring(0, i); // Check if package already loaded. Manifest man = res.getManifest(); definePackageInternal(pkgname, man, url); } // Now read the class bytes and define the class java.nio.ByteBuffer bb = res.getByteBuffer(); if (bb != null) { // Use (direct) ByteBuffer: CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); return defineClass(name, bb, cs); } else { byte[] b = res.getBytes(); // must read certificates AFTER reading bytes. CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); return defineClass(name, b, 0, b.length, cs); } }

这里面的核心逻辑代码都是本地方法。我们能看到的通常是一些基础的校验,比如准备阶段,解析阶段,初始化阶段都是本地方法

protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { // 预定义类信息 protectionDomain = preDefineClass(name, protectionDomain); // 定义类源码 String source = defineClassSourceLocation(protectionDomain); // 初始化类 Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); // 类定义后置处理 postDefineClass(c, protectionDomain); return c; }

这块代码了解即可。不用深入研究。

以上就是双亲委派机制的源码.

那么当下一次在遇到com.lxl.jvm.Math类的时候, 我们在AppClassLoader中就已经有了, 直接就返回了.

在来看一遍双亲委派机制的流程图

2.双亲委派机制详细解析及原理

三、为什么要有双亲委派机制? 两个原因: 1. 沙箱安全机制, 自己写的java.lang.String.class类不会被加载, 这样便可以防止核心API库被随意修改 2. 避免类重复加载. 比如之前说的, 在AppClassLoader里面有java/jre/lib包下的类, 他会加载么? 不会, 他会让上面的类加载器加载, 当上面的类加载器加载以后, 就直接返回了, 避免了重复加载.

我们来看下面的案例

加入, 我在本地定义了一个String类, 包名是java.lang.String. 也就是是rt.jar包下的String类的包名是一样的哈.

2.双亲委派机制详细解析及原理

如上图, 这是我们运行main方法, 会怎么样? 没错, 会报错

2.双亲委派机制详细解析及原理

下面分析一下, 为什么会报错呢?

还是看双亲委派机制的流程, 首先由AppClassLoader类加载器加载, 看看已经加载的类中有没有java.lang.String这个类, 我们发现, 没有, 找ExtClassLoader加载, 也没有, 然后交给引导类BootStrapClassLoader加载, 结果能不能找到呢? 当然可以了. 但是这个java.lang.String是rt.jar中的类, 不是我们自定义的类, 加载了rt.jar中的java.lang.String类以后, 去找main 方法, 没找到.....结果就抛出了找不到main方法异常.

所以说, 如果我们自己定义的时候, 想要重新定义一个系统加载的类, 比如String.class, 可能么? 不可能, 因为自己定义的类根本不会被加载

这就是双亲委派机制的第一个作用: 沙箱安全机制, 自己写的java.lang.String.class类不会被加载, 这样便可以防止核心API库被随意修改

双亲委派机制还有一个好处: 避免类重复加载. 比如之前说的, 在AppClassLoader里面有java/jre/lib包下的类, 他会加载么? 不会, 他会让上面的类加载器加载, 当上面的类加载器加载以后, 就直接返回了, 避免了重复加载.

第三个作用:全盘委托机制。比如Math类,里面有定义了private User user;那么user也会由AppClassLoader来加载。除非手动指定使用其他类加载器加载。也就是说,类里面调用的其他的类都会委托当前的类加载器加载。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjzpz.html