p; // from the non-null parent class loader
}
//父类无法加载该类,则由自己尝试加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
可见首先会检查该类是否已经被加载,若没有被加载,则会委托父加载器进行装载,只有当父加载器无法加载时,才会调用自身的findClass()方法进行加载。这样避免了子加载器加载一些试图冒名顶替可信任类的不可靠类,也不会让子加载器去实现父加载器实现的加载工作。
比如某个用户自定义的类加载器试图加载一个叫做“java.lang.String”的类,那么,该类会最终委派给启动类加载器 BootstrapClassLoader尝试加载,那么启动类加载器将加载Java API中的”java.lang.String”类而不会通过用户自定义的类加载器去获得和加载这个看上去不怀好意的冒名类。
但是仅仅依赖双亲委派是远远不够的,假设这个用户自定义的类加载器试图加载一个叫做“java.lang.Bomb”的危险类,而父类加载器无 法加载该类,那么加载工作将由用户定义的加载器负责实现。由于一个类的同一包内的类(和其子类)可以访问其protected成员,这个 “java.lang.Bomb”则可能访问可信任类的一些敏感信息,所以就必须将这个类与可信任类的访问域隔离,Java虚拟机只把这样彼此访问的特殊 权限授予由同一个类加载器加载的同一包内的类型,这样一个由同一个类加载器加载的、属于同一个包的多个类型集合称为运行时包。
2.命名空间
类加载体系为不同类加载器加载的类提供不同的命名空间,同一命名空间内的类可以互相访问,不同命名空间的类不知道彼此的存在(除非显式提供访问机制)。同一类可以再不同的命名空间内,但无法在同一命名空间内重复出现。
命名空间是这样定义的:实际完成加载类的工作的加载器为定义类加载器,而加载的双亲委托路径上的所有加载器为初始类加载器,某个加载器的命名空间就是所有以该加载器为初始类加载器的类所组成。
可以预见,子加载器的命名空间包括其父/祖先加载器的命名空间和只有自己才可以加载的类所组成。根据加载体系结构