package com.study;
class Singleton {
private static Singleton singleton = new Singleton();
public static int count1;
public static int count2 = 0;
private Singleton() {
count1++;
count2++;
}
public static Singleton getSingleton() {
return singleton;
}
}
public class MaiTest {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton.count1);
System.out.println(singleton.count2);
}
}
一个简单的程序,大家猜一下运行结果? 可能很多人觉得 结果是 输出 两个 1 ,但其实结果是 1、0
下面我们学习一下,整个 jvm 是如何加载 java 文件的
1 (class 变量加载流程)
类执行之前,有三个步骤:加载 连接 初始化
1 加载,查找并加载 类的二进制数据(加载到内存中),类加载器将,程序的 .class 文件加载到内存中
2 连接,验证 确保加载的正确性 // 文件是否正确,class文件也可能是 用户自己伪造的 class 文件,不是通过javac 生成的
准备 为类的静态变量 分配内存,并将其 初始化为默认值 // 类似 static int i 这种,其实是初始化 变量,然后赋值为0
解析 类中符号引用 转换为直接引用// 类B中调用A 的方法,hello ,B中会new 一个A 的对象,代码中就用 a 调用方法,但 其实 a 这个值一个符号引用,程序是不识别的,解析中,a 会变成一个 指向A 的指针,这样程序才能执行
3 初始化 为类的静态变量赋予正常的初始值// 给静态变量赋值
执行流程如下:
eq: 举例解释,比如 代码 public static int count1=1;
这个代码其实 分两个步骤,第一 是静态变量初始化, 方法区中分配 一块 区域,存储 count1 此时它的值为0
第二,就是赋值阶段,将数字1 赋值给 count1
2 类何时被初始化
java 程序对类的使用分两种: 主动使用,被动使用,java 程序对类 首次主动使用的时候,才会进行初始化
主动使用分为六种:
1 new A
2 A.count=1
3 A.sayHello()
4 反射 Class.forName(com.test.MyTest)
5 初始化类的子类
6 java 虚拟机启动的时候 标明启动类, java MyTest
3 类加载的最终产物 堆区的 class 对象
class 对象 封装了 类在方法区的数据结构,并且向java 程序员 提供了 访问方法区内的数据结构接口
何为类加载: 类加载 就是将 .class 文件中的二进制数据 读到内存中,放到 运行时数据区的方法区中,然后在 堆区创建一个 Class 对象,用了封装 类在方法区内 的数据结构
类加载过程如下: