1.类加载器
系统的类加载器分为引导类加载器或启动类加载器(Bootstrap ClassLoader)和自定义类加载器。其中自定义类加载器指的是所有继承于ClassLoader的类加载器。
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取其上层,扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@1b6d3586
//获取其上层
ClassLoader bootStrapClassLoader = parent.getParent();
System.out.println(bootStrapClassLoader);//null
//用户自定义类使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String类加载器,使用引导类加载器-->Java的核心类库都使用引导类加载器
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
第一个和第四个对象地址相同,说明用户自定义类都是使用系统类加载器进行加载。
有两个对象打印为null,说明这两个类都是使用引导类加载器进行加载。其中包括String类,得出结论:Java的核心类库都使用引导类加载器进行加载。
引导类加载器使用c和c++进行编写,并不继承自java.lang.ClassLoader。Java获取不到其对象,只有加载核心类库时才会用到。
AppClassLoader和ExtClassLoader都继承自ClassLoader。
关于ClassLoader
是一个抽象类,其后所有的类加载器都继承自ClassLoader(除了启动类加载器,使用C和c++编写,用于加载核心类库),凡是继承自ClassLoader的,都是用Java编写的。
2.双亲委派机制
引入
当我们自己建立了一个java.lang包下的String类时,在测试中如果new一个String,会发现new的这个String对象仍然会是Java核心API中的String,之所以创建的对象不会是我们自己写的这个java.lang包下的String类,就是因为双亲委派机制。
可以避免类重复和对核心API造成损坏。
如果我们尝试加载java.lang下的String类,由于引导类加载器负责加载包名为java、javax和sun包下的类,这时加载String类的任务就交给了引导类加载器,这时就不会向下委托给拓展类或系统类加载器。所以new的String对象就是核心API中的String类的对象。
在我们平时开发中,一般包名开头都是com.xxx,层层委托到引导类加载器时,引导类加载器发现开头不是java、javax、sun,就会委托给拓展类加载器,最后委托给系统类加载器进行加载。
当在我们自己定义的String类中试图用main方法输出语句,运行会发现报错。
public class String {
public static void main(String[] args) {
//报错,类 java.lang.String 中找不到 main 方法
System.out.println("自定义String类");
}
}
这是由于加载String类时,由前所述,会加载核心API中的String类,而这个类中并没有main方法,所以报错。
如果我们创建其他类,如java.lang.sgl类时,这个类运行报错,因为会有安全隐患。
表示两个类相同的必要条件:
1.包名及类名一致。
2.加载这个类的ClassLoader相同。