一、静态和非静态加载/执行顺序

  • 静态加载:静态分为静态变量和静态代码块,加载顺序位类中代码所处的先后顺序进行加载
  • 非静态加载: 按照非静态书写顺序加载/执行
  • 静态方法、实例方法只有在调用的情况下才回去执行
  • 静态加载中遇到需要加载非静态:先加载非静态在加载静态。

1.不涉及到父类子类的加载顺序

  • 首先将所有静态成员变量加载进来, 但是不赋值,JVM会根据属性的数据类型第一时间赋默认值
  • 然互再进行赋值,即加载静态变量一一为分配内存赋值,静态变量,静态块的加载,没有优先级之分,按照书写先后顺序加载

2.具体代码演示

public class JVMTest {
   
   static{
   	System.out.println("Main 方法所在静态代码块 static1");
   }
   
   public static void main(String[] args) {
   	 System.out.println("main start");
       A a = new A();
       System.out.println(A.width);
       System.out.println(a.width);
   }
   static{
   	System.out.println("Main 方法所在静态代码块 static2");
   }
}

class A{
   public static int width = 100;

   static{
       System.out.println("静态初始化类A");
       width = 30; 
   }

   public A(){
       System.out.println("创建A类的对象");
   }
}

运行结果如下:

Main 方法所在静态代码块 static1
Main 方法所在静态代码块 static2
main start
静态初始化类A
创建A类的对象
30
30

Java程序运行的时,第一件事情就是试图访问main方法,main相当于程序的入口,如果没有main方法,程序无法启动,main方法就是一个线程,因为main方法是一个特殊的静态方法,但仍然还是一个静态方法,此时JVM会加载main方法所在的类找到类中其他的静态部分。
上例中: 先找到main方法, 然后先加载main 方法所在的类JVMTest的静态属性, 静态代码块(按照顺序加载),即使静态代码块在main方法下面也要先加载静态代码块。然后执行 main方法

3.父类子类加载顺序

代码如下:

package ClassLoad;

 class JVMParent {
	
	 public static int width = 100;
	 
	 public static int count;
	 
	 {
		 System.out.println("parent no static code block :" + count);
	 }
	 
	 static{
		 System.out.println("parent static's count:" + count);
	 }
	 
	 JVMParent(int a){
		 System.out.println("parent init one parameter");
	 }
	 
	 JVMParent(){
		 System.out.println("parent init");
	 }
	 
}

public class JVMSon extends JVMParent {
	
	 
	 {
		 System.out.println("son no static code block :" + count);
	 }

	static {
		System.out.println("son static 1");
	}

	public static int count1;


	JVMSon() {
		System.out.println("son init:" + count);
	}

	static {
		System.out.println("son static 2");
	}

	public static void main(String[] args) {
		System.out.println("son main start");
		JVMSon a = new JVMSon();

	}

}

运行结果:

parent static's count:0
son static 1
son static 2
son main start
parent no static code block :0
parent init
son no static code block :0
son init:0

加载顺序:

  • 1.先加载包含main方法的类,加载类需要先加载父类静态变量,静态代码块(安装先后顺序加载)然后加载子类的静态变量,静态代码块。
  • 2.执行main方法
  • 3.main方法调用构造函数:父类代码块–> 父类构造函数–> 子类代码块–> 子类构造函数

4.JVM加载类过程

1.加载:

  • 类字节码文件从硬盘读入到内存中
  • 类加载器加载字节码文件 在方法区存放生成类对应的Class对象

2.连接:

  • 验证:对class等进行验证的过程;
  • 准备阶段:为静态变量开辟内存空间并赋上默认初始值;
  • 解析:符号化链接解析成实际链接(调用对象方法符号表示转变为方法的实际地址)

3.初始化:

  • 执行静态成员的初始化语句(为在连接部分中的准备阶段中已经分配内存空间和赋上默认值的静态成员赋值)
  • 执行静态语句块

类加载过程是先加载父类,然后再加载子类

类加载完毕后,如果要进行对象实例化就需要执行:

父类非静态成员初始化语句(包括代码块,按照在类定义中的顺序执行)->父类构造函数->子类非静态成员初始化语句(包括代码块,按照在类定义中的顺序执行)->子类构造方法

下面是总结的一个顺序,比较清楚:

有父类的情况

  1. 加载父类
    1.1 为静态属性分配存储空间并赋初始值
    1.2 执行静态初始化块和静态初始化语句(从上至下)
  2. 加载子类
    2.1 为静态属性分配存储空间
    2.2 执行静态初始化块和静态初始化语句(从上至下)
  3. 加载父类构造器
    3.1 为实例属性分配存数空间并赋初始值
    3.2 执行实例初始化块和实例初始化语句
    3.3 执行构造器内容
  4. 加载子类构造器
    4.1 为实例属性分配存数空间并赋初始值
    4.2 执行实例初始化块和实例初始化语句
    4.3 执行构造器内容