JVM 预定义类加载器 和 双亲委派机制

原文 - 关于Java类加载双亲委派机制的思考

JVM 预定义的三种类型类加载器:

类型 实现 负责加载类库的路径 开发者
启动类
加载器
本地代码 jre/lib 下面的类库,如 rt.jar 无法操作
扩展类
加载器
ExtClassLoader jre/lib/ext 下面的类库 或者
系统变量 java.ext.dir 指定位置中的类库
可以操作
系统类
加载器
AppClassLoader 系统类路径(CLASSPATH)中指定的类库 可以操作

注:我们平时自己定义的类就是在 CLASSPATH 路径指定的系统类。

双亲委派机制

某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

启动类 - father - 扩展类 - father - 系统类

实例

src 下定义 HelloWorld.java

csu/src/HelloWorld.java 
public class HelloWorld {
    public static void main(String[] args){
        System.out.println("HelloWorld");
    }
}

命令行运行 生成 字节码文件 HelloWorld.class

ShuaideMacBook-Pro:src shuai$ javac HelloWorld.java 
ShuaideMacBook-Pro:src shuai$ ls
HelloWorld.class    HelloWorld.java

运行字节码文件 输出 HelloWorld

ShuaideMacBook-Pro:src shuai$ java HelloWorld
HelloWorld

然后将字节码文件打成 jar 包,命名为 h.jar

ShuaideMacBook-Pro:src shuai$ jar -cvf h.jar HelloWorld.class
已添加清单
正在添加: HelloWorld.class(输入 = 419) (输出 = 285)(压缩了 31%)

再将 h.jar 移到 扩展类加载器 负责的类库路径下 jre/lib/ext

JVM 预定义类加载器 和 双亲委派机制_第1张图片

在这之后,改变 HelloWorld.java

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("输出内容更改");
    }
}

并重新编译执行

ShuaideMacBook-Pro:src shuai$ javac HelloWorld.java 
ShuaideMacBook-Pro:src shuai$ java HelloWorld
HelloWorld

因为 Java 的双亲委派机制, 扩展类加载器下有相应的 jar 包,所以输出内容不变。

面试题

能不能自己写个类叫 java.lang.System ?

答案:通常不可以,但可以采取另类方法达到这个需求。

解释:为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。

但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。

你可能感兴趣的:(JVM 预定义类加载器 和 双亲委派机制)