1. 前言
面试官:了不了解类加载器?
小王:支支吾吾后说了一句:"了解一点"......
面试官:那就来说说类加载器有几种分类?
小王:2种
面试官:具体都有哪些类加载器呢?
小王:引导类加载器、扩展类加载器、系统类加载器
面试官:这几种类加载器对应的职责是干什么的?
小王:......
面试官:来说说什么是双亲委派机制?
小王:......
面试结束后,小王心里特别难过:"为什么平时不用的东西,在面试中被频繁问到?这次的面试估计是没戏了"为了下次能够顺利通过面试,到家后的小王立刻打开了电脑,开始查阅相关资料。
2.类加载器
2.1 类加载器分类
类加载器一般分为:引导类加载器和自定义类加载器
2.1.1 引导类加载器
引导类加载器由C和C++实现,嵌入在JVM中
2.1.2 自定义加载器
自定义类加载器由JAVA代码实现,继承ClassLoader,常见的自定义类加载器有扩展类加载器:ExtClassLoader和系统类加载器:AppClassLoader
2.2 类加载器职责
2.1.1 引导类加载器
引导类加载器用于加载JAVA中的核心类库,如java.lang.String
// 获取加载String的类加载器:null
// 引导类加载器用于加载JAVA的核心类库
ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println("加载String的类加载器:" + classLoader3);
引导类加载器由C和C++实现,因此获取到的结果为null
2.1.2 自定义加载器
2.1.2.1 扩展类加载器
扩展类加载器用于加载jre/lib/ext下面的类
// 获取扩展路径
String extDir = System.getProperty("java.ext.dirs");
String[] extDirs = StringUtils.delimitedListToStringArray(extDir, ":");
for (String dir : extDirs) {
System.out.println("扩展路径:" + dir);
}
// 获取加载CalendarData_en_SG的类加载器:sun.misc.Launcher$ExtClassLoader@2eafffde
// 用于加载扩展路径下面的类
ClassLoader classLoader2 = CalendarData_en_SG.class.getClassLoader();
System.out.println("加载CalendarData_en_SG的类加载器:" + classLoader2);
2.1.2.2 系统类加载器
系统类加载器用于加载自定义的类
// 获取加载ClassLoaderTest的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
// 系统类加载器用于加载用户自定义的类
ClassLoader classLoader1 = ClassLoaderTest.class.getClassLoader();
System.out.println("加载ClassLoaderTest的类加载器:" + classLoader1);
2.3 双亲委派机制
2.3.1 示例
在介绍双亲委派机制前先来看一个示例,在项目中创建java.lang包,新建String.java类
public class String {
static {
System.out.println("自定义java.lang.String 被执行");
}
}
在其它包中创建StringTest.java类
public class StringTest {
static {
System.out.println("自定义java.lang.StringTest 被执行");
}
public static void main(String[] args) {
ClassLoader classLoader = java.lang.String.class.getClassLoader();
System.out.println("load String classLoader:" + classLoader);
StringTest.class.getClassLoader();
}
}
运行StringTest类中的main()方法,输出结果:
自定义java.lang.StringTest 被执行
load String classLoader:null
可以看到String类中的static代码块并没有执行
下面再来看一个示例,在String.java类中新增一个main()方法
public class String {
static {
System.out.println("自定义java.lang.String 被执行");
}
public static void main(String[] args) {
System.out.println("执行String的main()方法");
}
}
运行
String.java类中mian()方法,输出结果:Error: Main method not found in class java.lang.String, please define the main method as: public static void main(String[] args) or a JavaFX application class must extend javafx.application.Application
2.3.2 疑问
- 第一个示例为什么没有执行
String类中定义的static代码块? - 第二个示例执行
String类中的main()方法为什么会报错?
2.3.3 解惑
在第二章节中介绍了三种类加载器:引导类加载器、扩展类加载器、系统类加载器,它们存在着层级关系:
类加载器在加载一个类的时候并不会立刻去加载,而是向上委托,直到没有可以委托的类加载器后,自己才进行加载。
在理解这句话后来看看第一个问题:第一个示例为什么没有执行String类中定义的static代码块?
当我们要加载String这个类的时候,会一直向上委托,直到委托给启动类加载器,启动类加载器发现自己可以java.lang.String这个类,那么就进行加载,加载是的jdk自带的String类,并非我们定义的String类,因此static代码块没有被执行
了解第一个问题后,第二个问题也就迎刃而解了,jdk自带的String类中没有main()方法,报错也是理所当然的