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
在这之后,改变 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类根本没有机会得到加载。
但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。
网友评论