深入理解JVM-类加载及类加载器 (5)

下图:不是继承关系,是包含关系。

graph TD 根类加载器 --> 扩展类加载器 扩展类加载器 --> 系统类加载器 系统类加载器 --> 用户自定义类加载器

image-20200212210718684

线程上下文类加载器 作用:打破双亲委托机制。加载SPI提供的类。

image-20200212172526162

类加载器的双亲委托机制

一层一层的 让父类去加载,最顶层父类不能加载往下数,依次类推。

image-20200208231639491

image-20200212211132533

image-20200212211208653

image-20200208232907118

image-20200208232951439

能够成功加载自定义类加载器的系统类加载器 被称为定义类加载器。

所有能够返回Class对象引用的类加载器都被称为初始类加载器。

image-20200212211347665

image-20200212211400339

image-20200212211651815

public class MyTest7 { public static void main(String[] args) throws ClassNotFoundException { Class<?> c = Class.forName("java.lang.String"); System.out.println(c.getClassLoader()); // 返回针对于这个类的类加载器 Class<?> c1 = Class.forName("com.erwa.jvm.C"); System.out.println(c1.getClassLoader()); } } class C{ } > 输出结果为: > Task :MyTest7.main() null sun.misc.Launcher$AppClassLoader@659e0bfd getClassLoader()的 Javadoc: java.lang.Class<T> public ClassLoader getClassLoader() Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader. // 返回类的类加载器。一些实现可能使用null来表示引导类加载器。如果这个类是由引导类加载器加载的,那么这个方法在这样的实现中将返回null。 If a security manager is present, and the caller s class loader is not null and the caller s class loader is not the same as or an ancestor of the class loader for the class whose class loader is requested, then this method calls the security manager s checkPermission method with a RuntimePermission("getClassLoader") permission to ensure its ok to access the class loader for the class. If this object represents a primitive type or void, null is returned. //如果此对象表示基本类型或void,则返回null。 Returns: the class loader that loaded the class or interface represented by this object. Throws: SecurityException – if a security manager exists and its checkPermission method denies access to the class loader for the class. 对于final的理解深入 class FinalTest{ public static final int x=3; // final的话 就是编译期间的常量 static { System.out.println("FinalTest stataic block"); // 会输出吗?不会 } } public class MyTest8 { public static void main(String[] args){ System.out.println(FinalTest.x); } } 类的初始化顺序举例练习 class Parent { static int a = 3; static { System.out.println("parent stataic block "); } } class Child extends Parent{ static int b = 4; static { System.out.println("child static block "); } } public class MyTest9 { static { System.out.println("Mytest9 static block "); } public static void main(String[] args){ System.out.println(Child.b); } } > 输出结果: > Task :MyTest9.main() Mytest9 static block parent stataic block child static block 4 类的加载顺序: 1. MyTest9 2. Parent 3. Child // 这是一个好例子。 明白了。 类初始化的顺序 的练习。 // 每一个类,在初始化的时候,只会被初始化一次。 class Parent2 { static int a = 3; static { System.out.println("parent2 stataic block "); } } class Child2 extends Parent2{ static int b = 4; static { System.out.println("child2 static block "); } } public class MyTest10 { static { System.out.println("MyTest10 static block "); } public static void main(String[] args){ Parent2 parent2; // 不属于主动使用 System.out.println("- - -- - - "); parent2 = new Parent2(); // 主动使用, 初始化 Parent2 System.out.println("- - -- - - "); System.out.println(parent2.a); System.out.println("- - -- - - "); System.out.println(Child2.b); } } > 输出结果: > Task :MyTest10.main() MyTest10 static block - - -- - - parent2 stataic block - - -- - - 3 - - -- - - child2 static block 4 // 使用子类,访问父类中的方法。 等于是主动使用父类,而不是主动使用子类。 class Parent3 { static int a = 3; static { System.out.println("parent3 stataic block "); } static void doSomething(){ System.out.println("do something ") } } class Child3 extends Parent3{ static int b = 4; static { System.out.println("child3 static block "); } } public class MyTest11 { public static void main(String[] args){ System.out.println(Child3.a); // a 是定义在父类中的, 等于是 主动使用父类 Child3.doSomething(); } } > 预计结果: > Task :MyTest11.main() parent3 static block do something child3 static block 3 do something > 实际结果: > Task :MyTest11.main() parent3 stataic block 3 do something 错误原因: a 是定义在父类中的, 等于是 主动使用父类;并没有主动使用Child // 调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化 // 反射,是对类的主动使用,会初始化类 class CL{ static { System.out.println("Class CL "); } } public class MyTest12 { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = ClassLoader.getSystemClassLoader(); Class<?> clazz = loader.loadClass("com.erwa.jvm.CL");// 不会初始化CL类 System.out.println(clazz); System.out.println("- - - - - -"); clazz = Class.forName("com.erwa.jvm.CL"); // 反射会初始化CL类 System.out.println(clazz); } } > 输出结果: > Task :MyTest12.main() class com.erwa.jvm.CL - - - - - - Class CL class com.erwa.jvm.CL

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpszsp.html