第七章 虚拟机类加载机制
html
一、类加载的时机java
虚拟机的类加载机制:程序员
类初始化的五种状况:数组
被动引用:安全
父类:数据结构
package com.ecut.classload; public class SuperClass{ static { System.out.println("super init!"); } static int value = 1; }
子类:多线程
package com.ecut.classload; public class SubClass extends SuperClass{ static { System.out.println("sub init!"); } }
package com.ecut.classload; /** * 经过子类引用父类的静态字段,不会致使初始化 */ public class NotInitialization1 { public static void main(String[] args) { System.out.println(SubClass.value); } }
运行结果以下:jvm
........
[Loaded com.ecut.classload.SuperClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] [Loaded com.ecut.classload.SubClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] super init! 1
.......
package com.ecut.classload; /** * 经过数组定义来引用类,不会触发此类的初始化 * -XX:+TraceClassLoading */ public class NotInitializatio2 { public static void main(String[] args) { SuperClass[] superClasses = new SuperClass[10]; } }
运行结果以下:ide
....... [Loaded java.net.Proxy$Type from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded com.ecut.classload.SuperClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] [Loaded java.util.ArrayList$Itr from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded sun.net.NetHooks from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.net.Inet6Address$Inet6AddressHolder from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] ........
常量类:测试
package com.ecut.classload; public class ConstClass { public static final String HELLOWORD = "hello world"; static { System.out.println("const init !"); } }
测试类:
package com.ecut.classload; /** * 常量在编译阶段会存入调用类的常量池中,本质上并无直接引用到定义的常量的类,所以不会触发定义常量的类初始化 */ public class NotInitializatio3 { public static void main(String[] args) { System.out.println(ConstClass.HELLOWORD); } }
虽然在源码中引用了ConstClass类中的常量但其实在编译阶段已将此常量值存储在了main方法所在的常量池中,对常量的引用装换成了对自身常量池的引用。
二、类加载的过程
加载:
验证:
准备:
解析:
初始化:
三、类加载器
类与类加载器:
package com.ecut.classload; import java.io.IOException; import java.io.InputStream; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader myClassLoder = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return this.defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Object object = myClassLoder.loadClass("com.ecut.classload.ClassLoaderTest").newInstance(); System.out.println(object instanceof com.ecut.classload.ClassLoaderTest); } }
运行结果以下:
false
类加载器的分类:
双亲委派模型:
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,检查类是否已经加载。 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false);//看父类加载器有没有加载该类(父委托机制) } else { c = findBootstrapClassOrNull(name);//父类加载器为空,看根加载器(Bootstrap Loader)有没有加载 } } catch (ClassNotFoundException e) { //若是类没有发现抛出ClassNotFoundException } if (c == null) { //若是仍然没有找到,而后调用findClass为了找到类。 long t1 = System.nanoTime(); c = findClass(name); // 这是定义类装入器;记录统计数据 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
先检查是否被加载过,若没有被加载过调用父类的loadclass方法,若父加载器为空则默认使用启动类加载器为父加载器,若是父加载器加载失败就抛出ClassNotFoundException ,再调用本身的findClass方法进行加载。
破坏双亲委派机制模型:
转载请于明显处标明出处