标签:property 声明 返回 path pen 输出 类加载器 运行时 初始化块
当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对类进行初始化。如果没有意外,jvm将会连续完成这三个步骤,有时也把这三个步骤统称为类的加载和类初始化。
类加载指的是将类的class文件读入内存,并且为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,都会为之创建一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建类加载器。
当类被加载之后,系统会为之生成一个Class对象,接着将进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。连接阶段分为三个步骤:
a.验证:验证阶段用于验证被加载的类是否有正确的内部结构,并和其他类协调一致。
b.准备:准备阶段负责为类变量分配内存,并且设置默认的初始值。
c.解析:解析阶段会让JVM检查类文件中所引用的类型是否都是已知的类型,如果有运行时未知的类,那么它们也会被加载进来。
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化,在java类中对类变量进行初始化有两种方式:1.声明类变量时指定初始值。2.使用静态初始化块对类变量指定初始值。
类变量和静态初始化块的执行优先级是同级的,例如:
public class Test { static{ n=10; } static int n=9; public static void main(String[] args) { System.out.println("n="+n);//9 } }
这里的输出值是9,并非10,因为静态初始化块和类变量是同级的。当程序加载、连接完成以后,进入准备初始化阶段,这个时候n为0,程序按照先后循序首先使用10为n赋值,然后再使用9为n赋值。
经过上面的介绍,我们已经知道一个对象的建立需要经历加载、链接、初始化。在类加载的时候,需要使用类加载器,接下来介绍类加载器的作用。
类加载器负责将.class文件加载到内存中,并为之生成java.lang.Class对象,这是后面讲到的java反射机制的基础。
当JVM启动时,会形成由三个类加载器组成的类加载器结构。
Bootstrap ClassLoader:根类加载器
Extension ClassLoader:扩展类加载器
System ClassLoader:系统类加载器
如果用户自定义了类加载器,那么还有用户类加载器。除了根类加载器外所有的类加载器都是java.lang.ClassLoader的子类,并且这些类加载器是具有层次结构的。
如图:
根类加载器:负责加载java的核心类。
扩展类加载器:负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext或者java.ext.dirs指定的目录)中的jar包。
系统类加载器:负责在JVM启动时,加载来自java命令的-classpath选项,或是java.class.path系统属性,或是CLASSPATH环境变量所指定的JAR包和类路径。
public static void main(String[] args) throws IOException { System.out.println("//系统类加载器"); ClassLoader systemLoader=ClassLoader.getSystemClassLoader(); System.out.println("系统类加载器:"+systemLoader); /* 获得系统类加载器的加载路径 * 通常为CLASSPATH环境变量指定的值。 * 如果没有指定CLASSPATH环境变量,那么默认以当前路径作为系统类的加载路径。 */ Enumeration<URL> em1=systemLoader.getResources("."); while(em1.hasMoreElements()){ System.out.println(em1.nextElement()); } System.out.println("//扩展类加载器"); ClassLoader extensionLoader=systemLoader.getParent();//系统类加载器的父类就是扩展类加载器 System.out.println("扩展类加载器:"+extensionLoader); System.out.println("扩展类加载器的加载路径:"+System.getProperty("java.ext.dirs")); System.out.println("扩展类加载器的父加载器:"+extensionLoader.getParent());//获得根加载器 System.out.println("//根类加载器"); URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs(); for(int i=0;i<urls.length;i++){ System.out.println(urls[i].toExternalForm()); } }
打印结果为:
//系统类加载器 系统类加载器:sun.misc.Launcher$AppClassLoader@addbf1 file:/D:/workspace/pc141/Test/bin/ //扩展类加载器 扩展类加载器:sun.misc.Launcher$ExtClassLoader@42e816 扩展类加载器的加载路径:D:\App\jdk\jre1.6\lib\ext;C:\Windows\Sun\Java\lib\ext 扩展类加载器的父加载器:null //根类加载器 file:/D:/App/jdk/jre1.6/lib/resources.jar file:/D:/App/jdk/jre1.6/lib/rt.jar file:/D:/App/jdk/jre1.6/lib/sunrsasign.jar file:/D:/App/jdk/jre1.6/lib/jsse.jar file:/D:/App/jdk/jre1.6/lib/jce.jar file:/D:/App/jdk/jre1.6/lib/charsets.jar file:/D:/App/jdk/jre1.6/lib/modules/jdk.boot.jar file:/D:/App/jdk/jre1.6/classes
从上面的打印结果可以看出,系统类加载器的父加载器是扩展类加载器,扩展类类加载器的父加载器是根加载器,但是为什么调用扩展类加载器的getParent()却返回null呢?这是因为根加载器比较特殊,它不是ClassLoader的子类。
标签:property 声明 返回 path pen 输出 类加载器 运行时 初始化块
原文地址:https://www.cnblogs.com/leiblog/p/10447230.html