【Java基础】java类加载过程与反射机制

1.类的加载、连接和初始化

当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对类进行初始化。如果没有意外,jvm将会连续完成这三个步骤,有时也把这三个步骤统称为类的加载和类初始化。

1.1 类的加载

类加载指的是将类的class文件读入内存,并且为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,都会为之创建一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建类加载器。

1.2 类的连接

当类被加载之后,系统会为之生成一个Class对象,接着将进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。连接阶段分为三个步骤:
a.验证:验证阶段用于验证被加载的类是否有正确的内部结构,并和其他类协调一致。
b.准备:准备阶段负责为类变量分配内存,并且设置默认的初始值。
c.解析:解析阶段会让JVM检查类文件中所引用的类型是否都是已知的类型,如果有运行时未知的类,那么它们也会被加载进来。

1.3 类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化,在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赋值。
经过上面的介绍,我们已经知道一个对象的建立需要经历加载、链接、初始化。在类加载的时候,需要使用类加载器,接下来介绍类加载器的作用。

2.类加载器

类加载器负责将.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());
        }
    }
View Code

打印结果为:

//系统类加载器
系统类加载器: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的子类。

猜你喜欢

转载自www.cnblogs.com/leiblog/p/10447230.html