• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Java类的加载和初始化

类加载机制 winrains 来源:匠丶 1年前 (2019-08-31) 47次浏览

为什么说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
    }
}

作者:匠丶

来源:https://www.jianshu.com/p/2000f9d805ef


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)