加载机制系列
类加载机制(一)
类加载机制(二)
类加载机制(三)
类加载机制
1.JVM把class文件加载到内存,对数据进行校验,解析,初始化 最终形成JVM可以直接使用的过程
加载
将class文件字节码加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个该类的java.lang.Class对象,作为方法区类数据的访问入口
该过程需要类加载器的参与。
链接
- 验证
保证加载类信息符合JVM标准,没有安全方面的问题 - 准备
正式为变量(static变量)分配内存并设置类变量初始化的阶段,这些内存在方法区进行分配 - 解析
虚拟机常量池内的符号引用替换为直接引用的过程。
初始化
- 初始化阶段是执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是有编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中语句合并产生的。
- 当初始化一个类的时候,如果发现其父类没有初始化,则需先初始化其父类
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中正确加锁和同步,既类的唯一性。
- 当访问一个JAVA类的静态域时,只有真正声明这个域的类才会被加载。
demo
public class Demo01 {
public static void main(String[] args) {
A a= new A();
System.out.println(A.width);
}
}
class A{
public static int width =100;
static {
System.out.println("静态初始化类");
width = 300;
}
public A() {
System.out.println("创建A对象");
}
}
运行结果:
静态初始化类
创建A对象
300
内存加载分析
1.先把demo01的代码字节码加载到方法区
2.同时在堆中生成java.lang.class对象(反射对象)
3.把A的代码字节码加载到方法区
4.同时在堆中生成java.lang.class对象(反射对象)
5.栈中main方法栈 中A a=null
调用new A()
6.A()方法栈
7.实例化A对象在堆中生成对象A 把main方法栈中 a的地址引用指向堆中A对象
类的加载全过程
类的主动引用(会初始化)
- new 一个类的对象
- 调用类的静态成员(除了final修饰的常量)和静态方法
- 使用java.lang.reflect包的反射调用
- 虚拟机启动main方法所在的类
- 当调用一个类时候,如果其父类没有被初始化则先初始化其父类
类的被动引用(不会初始化)
- 当访问一个静态域时,只有真正生命这个域的类才会被初始化,如:通过子类引用父类的静态变量时候,不会导致子类初始化
- 通过数组定义类引用 不会被初始化
- 引用常量不会触发此类的初始化 (常量在编译阶段已存入调用该类的常量池中了)
Demo
public class Demo01 {
static {
System.out.println("静态初始化类Demo01");
}
public static void main(String[] args) {
System.out.println("Demo01的main() 方法");
A a = new A();
System.out.println(A.width);
}
}
class A_Father {
public static int father_width=10;
public final static int father_final_test=333;
static {
System.out.println("静态初始化类A_father");
}
}
class A extends A_Father {
public static int width = 100;
static {
System.out.println("静态初始化类A");
width = 300;
}
public A() {
System.out.println("创建A对象");
}
}
结果:
先初始化A的父类 A_Father
静态初始化类Demo01
Demo01的main() 方法
静态初始化类A_father
静态初始化类A
创建A对象
300
修改main方法
public static void main(String[] args) {
// System.out.println("Demo01的main() 方法");
// A a = new A();
//调用A的静态域
System.out.println(A.width);
}
结果:A类被加载初始化
静态初始化类Demo01
静态初始化类A_father
静态初始化类A
300
再次修改直接用反射调用
public static void main(String[] args) throws Exception{
// System.out.println("Demo01的main() 方法");
// A a = new A();
//调用A的静态域
//System.out.println(A.width);
//反射调用
Class.forName("com.org.test.init.A");
}
结果:A类被加载初始化
静态初始化类Demo01
静态初始化类A_father
静态初始化类A
调用常量:
public static void main(String[] args) throws Exception{
// System.out.println("Demo01的main() 方法");
// A a = new A();
//调用A的静态域
//System.out.println(A.width);
//反射调用
//Class.forName("com.org.test.init.A");
//调用常量
System.out.println(A_Father.father_final_test);
}
结果:类没有加载初始化
静态初始化类Demo01
333
调用父类静态域
public static void main(String[] args) throws Exception{
// System.out.println("Demo01的main() 方法");
// A a = new A();
//调用A的静态域
//System.out.println(A.width);
//反射调用
//Class.forName("com.org.test.init.A");
//调用常量
//System.out.println(A_Father.father_final_test);
//调用父类静态变量
System.out.println(A_Father.father_width);
}
结果:只有父类被加载初始化
静态初始化类Demo01
静态初始化类A_father
10
数组引用:
public static void main(String[] args) throws Exception{
// System.out.println("Demo01的main() 方法");
// A a = new A();
//调用A的静态域
//System.out.println(A.width);
//反射调用
//Class.forName("com.org.test.init.A");
//调用常量
//System.out.println(A_Father.father_final_test);
//调用父类静态变量
//System.out.println(A_Father.father_width);
//数组变量引用
A[] as= new A[10];
}
结果:类不会被加载
静态初始化类Demo01