Java类的加载和初始化

作者: 匠丶 | 来源:发表于2018-09-17 23:29 被阅读1991次

    为什么说Java是跨平台语言

    Java语言的运行环境是在Java虚拟机中。 Java虚拟机消除了各个平台之间的差异,只要操作系统平台下安装了Java虚拟机,那么使用Java开发的东西都能在其上面运行。如下图所示:



    Java虚拟机对各个平台而言,实质上是一个可执行程序。例如在windows平台下,java虚拟机就是一个java.exe进程而已。

    JVM虚拟机启动和加载类过程

    下面以一个简单的java程序,分析虚拟机启动过程。

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("hello world");
        }
    }
    

    当我们运行上面程序时,将会启动一个虚拟机进程,不管Java程序有多么复杂,启动了多少线程,它们都将处于该Java虚拟机进程里。具体过程如下:

    1、根据JVM的内存配置,为JVM申请特定大小的内存空间

    2、创建一个引导类加载器实例

    JVM申请好内存空间后,JVM会创建一个引导类加载器(Bootstrap Classloader)实例,引导类加载器是使用C++实现的,负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.String, java.lang.Object等。引导类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib 下的jar包和配置,然后将这些系统类加载到方法区内。

    方法区用于存储已经被虚拟机加载过的类信息、常量、静态变量、即时编译器编译出的代码等数据。


    Class是一个实实在在的类,在包 java.lang 下。它跟我们自己定义的类一样,是一个实实在在的类,Class对象就是这个Class类的实例了。在Java里,所有的类的根源都是Object类,Class也不例外,它是继承自Object的一个特殊的类,它内部可以记录类的成员、接口等信息。

    3、创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader

    上述步骤完成,JVM基本运行环境就准备就绪了。此时,JVM虚拟机会调用已经加载在方法区的类sun.misc.Launcher 的静态方法getLauncher(), 获取sun.misc.Launcher 实例:

    sun.misc.Launcher launcher = sun.misc.Launcher.getLauncher(); //获取Java启动器
    ClassLoader classLoader = launcher.getClassLoader();          //获取类加载器ClassLoader用来加载class到内存来
    

    sun.misc.Launcher 使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。在Launcher的内部,其定义了两个类加载器(ClassLoader),分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader,这两个类加载器分别被称为拓展类加载器(Extension ClassLoader) 和 应用类加载器(Application ClassLoader).如下图所示:



    此时的 launcher.getClassLoader() 方法将会返回 AppClassLoader实例。
    当AppClassLoader加载类时,会首先尝试让父加载器ExtClassLoader进行加载,如果父加载器ExtClassLoader加载成功,则AppClassLoader直接返回父加载器ExtClassLoader加载的结果;
    如果父加载器ExtClassLoader加载失败,AppClassLoader则会判断该类是否是引导的系统类(即是否是通过Bootstrap类加载器加载,这会调用Native方法进行查找);
    若要加载的类不是系统引导类,那么ClassLoader将会尝试自己加载,加载失败将会抛出“ClassNotFoundException”。

    上面讨论的应用类加载器AppClassLoader的加载类的模式就是我们常说的双亲委派模型(parent-delegation model).
    对于某个特定的类加载器而言,应该为其指定一个父类加载器,当用其进行加载类的时候:

    1. 委托父类加载器帮忙加载;
    2. 父类加载器加载不了,则查询引导类加载器有没有加载过该类;
    3. 如果引导类加载器没有加载过该类,则当前的类加载器应该自己加载该类;
    4. 若加载成功,返回 对应的Class<T> 对象;若失败,抛出异常“ClassNotFoundException”。

    4、使用类加载器ClassLoader加载HelloWorld类

    通过 launcher.getClassLoader()方法返回AppClassLoader实例,接着就是AppClassLoader加载HelloWorld类的时候了。

    5、加载完成时候JVM会执行HelloWorld类的main方法入口

    6、java程序运行结束,JVM销毁

    类的初始化

    在Java类中对类变量指定初始值有两种方式,一是声明类变量时指定初始值;二是使用静态初始化块为类变量指定初始值。
    JVM会按照这些语句在程序中的排列顺序依次执行它们。另外,如果类的父类还没初始化,会先初始化父类。例如下面的类:

    public class Test {
        static {
            b = 6;
            System.out.println("---------");
        }
        static int a = 5;
        static int b = 9;
        static int c;
    
        public static void main(String[] args) {
            System.out.println(Test.b);//结果为9
        }
    }
    

    相关文章

      网友评论

        本文标题:Java类的加载和初始化

        本文链接:https://www.haomeiwen.com/subject/gszdnftx.html