双亲委派的定义
类加载器的父亲委托机制(双亲委派机制)
类加载器加载一个类时,会优先交给其父的加载器加载,父加载器找不到时会返回给子加载器
常用的类加载器包含以下
Bootstrap ClassLoader: 默认加载 rt.jar下的类,可以通过 -Xbootclasspath选项指定加载 jar包
Extension ClassLoader: 其包含 Bootstrap ClassLoader,默认加载 ext下的jar包 ,可以通过 -Djava.ext.dirs指定目录加载
App ClassLoader: 默认加载CLASSPATH,也可以加载-Djava.class.path所指定目录下的jar包
Customer ClassLoader: 通过ClassLoader自定义指定加载class
获取系统的ClassLoader以及其父的ClassLoader
通过打印的结果可以知道,BootStrapClassLoader是获取不到的,系统的ClassLoader为AppClassLoader,扩展ClassLoader为ExtClassLoader
获取ClassLoader的途径
获取ClassLoader的途径
获取当前类的ClassLoader clazz.getClassLoader
获取当前线程上下文的ClassLoader Thread.currentThread().getClassLoader();
获取系统的ClassLoader ClassLoader.getSystemClassLoader
获取调用者的ClassLoader DriverManager.getCallerClassLoader()
通过上面的结果可以得出以下结论:
默认情况下 线程上下文的类加载器为当前在加载线程的类的类加载器一致,即就是加载Test14类的加载器
获取class资源的URL,根据URL可以获取当前资源的完整路劲
获取数组的类加载器
以下段落来自doc文档
Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime. The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
这段话描述的意思为,如果是数组类型,那么数组类型的类加载器与数组里面的元素的类加载器一致,原生类型的话没有类加载器
String类型是有BootstrapClassLoader加载的,BootStrapClassLoader是获取不到的所以为空
Test15是由AppClassLoader加载的,所以test15s这个数组的类加载器也为AppClassLoader
原生类型数组的类加载器为空
自定义ClassLoader案列
public class Test16 extends ClassLoader {
private String classLoaderName;
private String fileExtention=".class";
private String path;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Test16(String classLoaderName){
//默认情况下,其父ClassLoader为getSystemClassLoader即AppClassLoader
super();
this.classLoaderName=classLoaderName;
}
public Test16(ClassLoader classLoader,String classLoaderName){
//指定一个父的ClassLoader
super(classLoader);
this.classLoaderName=classLoaderName;
}
@Override
public Class findClass(String name) throws ClassNotFoundException {
System.out.println("find class invoked:"+name);
System.out.println("class loader name:"+this.classLoaderName);
byte []data=this.loadClassData(name);
return this.defineClass(name,data,0,data.length);
}
private static void test(ClassLoader classLoader) throws Exception {
Class clazz=classLoader.loadClass("test.Test13");
Object object=clazz.newInstance();
System.out.println(object);
}
private byte[] loadClassData(String name){
InputStream in=null;
byte []data=null;
ByteArrayOutputStream baos=null;
try {
name=name.replace(".","/");
in=new FileInputStream(new File(this.path+name+fileExtention));
baos=new ByteArrayOutputStream();
int ch=0;
while((ch = in.read()) != -1){
baos.write(ch);
}
data=baos.toByteArray();
return data;
}catch (Exception e){
e.printStackTrace();
}finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
可以发现并没有打印自定义ClassLoader中的findClass方法
原因为因为自定义的类加载Test16其父加载器为系统类加载器
当前工程下的class文件都是由系统类加载器加载的,所以不会执行自定义的类加载器定义的方法
删除classPath下的Test13,并且将Test13移到D盘的某个目录
执行下列的代码
可以发现执行了我们自定义的ClassLoader的findClass方法了
Class类的卸载
由用户自定义的类加载器所加载的类是可以被卸载的
通过制定-XX:TraceClassUnloading参数打印卸载信息
也可以通过jisualvm查看类的卸载信息
类加载器的命名空间
命名空间
每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的2个类
在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的2个类
通过上面的案列可以发现,2个不同的类加载器可以加载同一个包下的同一个类。
通过上面的案列可以发现,如果指定了父加载器为classLoader那么就不会执行自定义加载器的findClass方法了
因为类加载器加载类的时候会判断是否加载过,因为classLoader作为父类加载器已经加载过了
所以子加载器不会在次加载了
在运行期间,一个Java类是由该类的完全限定名(binary name,二进制名)和用于加载该类的定义类加载器(defining loader)所共同决定的。如果同样名字(即相同的限定名)的类是由两个不同的加载类所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从相同的位置加载也是如此。
类加载器命名空间的重要说明
关于命名空间的重要说明
1.子加载器加载的类能够访问父加载器加载的类
2.父加载器加载的类不能访问子加载器加载的类
发现并没有加载Cat
演示下列操作,此处省略图片,,最终得出上述关于命名空间的重要说明的结论
1.演示在classPath下删除 Sample.class,不删除Cat.class的效果 ,Sample会由Test16加载,Cat是由AppClassLoader加载
如果此时在Cat中获取Sample类会报错,所以此时 Test16可以访问 Cat,而Cat不能访问Sample
2.演示在classPath下删除 Cat.class,不删除Sample.class的效果 ,Sample会由AppClassLoader加载
此时因为Cat是在Sample内部初始化的 Cat必须也只能 AppClassLoader或者其父加载器加载