为什么说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).
对于某个特定的类加载器而言,应该为其指定一个父类加载器,当用其进行加载类的时候:
- 委托父类加载器帮忙加载;
- 父类加载器加载不了,则查询引导类加载器有没有加载过该类;
- 如果引导类加载器没有加载过该类,则当前的类加载器应该自己加载该类;
- 若加载成功,返回 对应的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
}
}