一、JVM何时被终止

1、程序运行到最后正常终止
2、程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序
3、程序执行过程中遇到未捕获的异常或错误而结束
4、程序所在的平台强制结束了JVM进程


注:当我们调用java命令运行某个java程序时,该命令将会启动一个java虚拟机进程,不过该Java程序有多么复杂,该程序启动了多少线程,他们都处于Java虚拟机进程里。同一个jvm的所有线程、变量等都处于同一个进程(jvm进程)里,他们都使用该JVM进程的内存区。


二、类的初始化


类的初始化包括:类加载——连接——初始化
当程序主动使用某个类是,如果如果该类还没有被加载到内存中,则系统通过加载、连接、初始化3个步骤对该类进行初始化。有时把这3个步骤统称为类的初始化。


类的加载:将类的class文件读入内存,并为之创建一个Class对象
类的连接:类的连接分为——验证、准备、解析
验证——验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备——类准备阶段则负责为类的静态属性分配内存,并设置默认初始值
解析——将类的二进制数据中的符号引用替换成直接引用
类的初始化:对静态属性进行初始化


注1:类的初始化要和类加载中的准备阶段区分开,准备阶段是——为类的静态属性分配内存,并设置默认初始值。举例说明类的初始化与类连接中准备阶段的区别:
class A{

static int a;
static int b=6;
/*
*类的连接准备阶段:为a、b分别分配内存空间,并设默认初始值为0
*初始化阶段:设置b=6
*/

}
注2:要区别类的初始化和对象的初始化。以前我一直错以为同过new 创建对象的过程就是类的初始化。随后还会讲解,这里不明白不要紧。

三、JVM初始化一个类包含如下几个步骤:

1、假如这个类还没有被加载和连接,则程序先加载并连接该类。
2、假如该类的直接父类还没有还没有被初始化,则先初始化其直接父类。
3、假如类中有初始化语句,则系统依次执行这些初始化语句。

当执行到第2步时,系统对该类的直接父类初始化也遵循1~3步,如果该类直接父类还有直接父类,则系统重复1~3步初始化父类

四、类初始化的时机

1、创建类的实例。为某个类创建实例的方式包括:使用new操作符来创建实例,通过反射来创建实例,通过反序列化的方式创建实例。
2、调用某个类的静态方法
3、访问某个类或接口的静态属性或为该静态属性赋值。
4、利用反射创建某个类或接口的java.lang.Class对象。例如:Class.forName("Person");
4、初始化某个类的子类时。
5、用java命令运行某个类

注:当我们用文字描述时,或许你很难读懂,所以必须通过自己打代码来测试才能使你更深入理解。突然想起一句话:即使再好的书籍或老师,讲的知识始终属于他们,你只有通过亲自尝试(多练习,多理解),才能成为自己的知识。(特别对于技术类的知识,只有通过亲自实践,才能记忆深刻,才能转变成自己的知识)

例1:

A类: 

 public class A { 

static{ 

System.out.println("我是A类静态方法"); 

} 

public A() { 

System.out.println("我是A类构造方法"); 

} 

 } 


 B类: 

 public class B extends A { 

static{ 

System.out.println("我是B类静态方法"); 

} 

public B() { 

System.out.println("我是B类构造方法"); 

} 

 } 


 测试类: 



 public class Test { 



public static void main(String[] args) { 

B b1 = new B(); // 标记1 

B b2 = new B(); // 标记2 

} 



 }




运行测试类结果:当看到测试类时先自己想想会打印什么结果,然后再自己运行是否与你想的运行结果一样呢
















我是A类静态方法
我是B类静态方法
我是A类构造方法
我是B类构造方法
我是A类构造方法
我是B类构造方法

通过上面知识解释结果:

程序运行到“标记1”处,JVM首先检查B类是否有被加载和连接,则程序先加载并连接该B类。B的直接父类A类是否被初始化,则先初始化其直接父类(A类)。(结合三,jvm初始化类的几个步骤,一个类首先被初始化始终是Object类,因为Object是所有类的默认父类。如果类中有初始化语句,则执行这些初始化语句)
所以程序依次输出:

我是A类静态方法
我是B类静态方法

类初始化完成之后,对象的初始化。所以依次输出:

我是A类构造方法
我是B类构造方法

对象初始化完成后,运行到标记2处:

重复上面的步骤,首先检查B类是否被加载和连接,是就不会在加载连接。检测B类的父类A是否被初始化,是被初始化。在同一个jvm虚拟中,如果一个类被同一个加载器加载并初始化过,则他不会被初始化第二次。所以不会在输出:


我是A类静态方法
我是B类静态方法


然后再次初始化b2对象,(因为同过new创建了对象,所以要再次初始化B所产生的对象)


例2:类初始化与对象初始化的联系


在同一个jvm虚拟机中,同一个类加载器加载同一个类时,则该类只能——初始化类一次。但是每次通过new创建对象的时候都会初始化对象。

可以通过三说明一下类初始化和对象初始化时机的不同。

A类: 

 public class A { 


static int n = 1; 


static{ 

System.out.println("我是A类静态方法"); 

} 

public A() { 

System.out.println("我是A类构造方法"); 

} 

 } 


 测试类: 


 public class Test { 


public static void main(String[] args) { 

int t=A.a; 
 

} 

 }



运行结果:


我是A类静态方法


通过上面知识解释结果:使用四——类初始化的时机来验证类初始化时机与对象初始化时机不同。


上面说过,通过访问类的静态属性会初始化类,所以会输出:


我是A类静态方法


但是并不会初始化对象。通过new创建对象时,jvm首先会初始化类,然后在初始化对象。可以自己编写测试类来分别验证上面文字的正确性,只有通过自己验证过,并理解吸收的知识才属于你自己,否则从别人获取的知识始终属于别人。


注:如果一个属性用final修饰,即如: final static int a=9;通过类的调用属性时,不会导致类的初始化。因为final修饰会被当成常量处理,还有其他情况,再次不做介绍。