引言:

首先附上三段代码:

1
2
3
public class A {
public static int a = 6;
}
1
2
3
4
5
6
7
public class ATest1 {
public static void main(String[] args) {
A a = new A();
a.a++;
System.out.println(a.a);
}
}
1
2
3
4
5
6
public class ATest2 {
public static void main(String[] args) {
A b = new A();
System.out.println(b.a);
}
}

当我们运行完 ATest1 之后 再次运行 ATest2 将会发现 静态变量a的值仍是6,因为在 JVM 中,第一次运行JVM会自动结束,因此对静态变量A保存的数据将会全部丢失,第二次运行JVM将会重新初始化变量,所以值仍为6

结论:

  1. 每次运行某个Java程序,命令将会启动一个JVM进程,并且该程序中的线程将处于同一个进程中,都使用该JVM内存区
  2. 倘若系统出现一些情况,JVM进程将会被终止
    1. 程序运行到最后结束
    2. 程序运行中遇到错误或者未捕获的异常
    3. 程序中使用System.exit() 或 Runtime.getRuntime().exit() 将会结束程序
    4. JVM被平台终止

类加载(类初始化)的三个步骤

加载

当我们启动一个 JVM ,系统将会通过 加载 连接 初始化 三个步骤 对该类进行初始化!

加载:将类的Class文件读入内存,并为之创建一个java.lang.Class对象~~~~也就是说,当我们使用任何类的时候,系统都会为之建立java.lang.Class对象!加载是由类加载器完成的,当然也可以通过继承来创建类加载器!

当类被成功加载到 JVM 中,系统将会生成一个 java.lang.Class对象, 从而进入连接阶段


连接

连接:负责把加载到的二进制数据合并到JRE中,分为如下3个阶段

连接的三个阶段

  1. 验证:用于检验被加载的类是否有正确的内部结构
  2. 准备:负责为类变量分配内存,并设置默认初始值
  3. 解析:将类的二进制数据中的符合引用替换成直接引用

初始化

初始化:对类变量进行初始化

初始化两种方式:

  1. 静态代码块给类变量指定初始值
  2. 声明类变量的时候指定初始值

注意事项:

  1. 如果类还没有被加载和连接,则程序先加载并连接该类
  2. 如果该类的直接父类没有初始化,则先初始化直接父类
  3. 如果类中有初始化语句,则依次执行初始化语句

类初始化的时机

  1. 创建类的实例
  2. 调用某个类的类方法
  3. 调用某个类或接口的类变量
  4. 使用反射方式强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java命令运行某个主类的时候

    对于一个 final 型 变量,编译时就可以确定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyTest{

static {
System.out.println("静态初始化快。。。");
}

static final String compileConstant = "final变量";
}

public class Test2 {

public static void main(String[] args) {

System.out.println(MyTest.compileConstant);

}

}

结果如下
final变量