加载/执行顺序:
牢记一点:
静态和非静态分开处理
使用到静态加载时,静态又分为: 静态变量, 静态代码块, 其中加载顺序是按照类中书写的先后顺序加载的
非静态加载顺序: 按照非静态书写顺序加载/执行
静态方法,实例方法只有在调用的时候才会去执行
当静态加载中遇到需要加载非静态的情况: 先加载非静态再加载静态。
下面两种情况的加载顺序
不涉及到父类子类的情况:
1) 首先将所有静态成员变量加载进来, 但是不赋值,JVM会根据属性的数据类型第一时间赋默认值
2)然互再进行赋值,即加载静态变量一一为分配内存赋值,静态变量,静态块的加载,没有优先级之分,按照书写先后顺序加载
特殊情况: 静态变量是加载类本生的实例,会比较麻烦,稍后讲解
涉及到父类的情况
父静态-->子静态 加载时不涉及构造方法,只有使用都new才会涉及到构造方法
下面将具体讲述程序执行顺序
1. main第一句是否先执行
Java程序运行时,第一件事情就是试图访问main方法,因为main相等于程序的入口,如果没有main方法,程序将无法启动,main方法更是占一个独立的线程,找到main方法后,是不是就会执行mian方法块里的第一句话呢?不是
因为main方法虽然是一个特殊的静态方法,但是还是静态方法,此时JVM会加载main方法所在的类,试图找到类中其他静态部分,即首先会找main方法所在的类。
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
上例中: 先找到main方法, 然后先加载main 方法所在的类JVMTest的静态属性, 静态代码块(按照顺序加载),即使静态代码块在main方法下面也要先加载静态代码块。然后执行 main方法
2. 静态变量声明一定放在使用前面
3. 父类,子类加载顺序
public 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方法, 先要加载包含Main方法的类, 加载类就先加载父类静态变量, 静态代码块(按照书写先后顺序)--》 子类静态变量,静态代码块
2) 执行main方法
3) main 方法中调用有构造函数
父类代码块--》 父类构造函数--》子类代码块--》子类构造函数
4. 一个比较复杂的例子,如果下面的例子理解了,就撤离理解了Java的执行顺序
public class Text {
public static int k = 10;
public int a = print("a");
public static int b = print("b");
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
public int m = print("m");
{
print("构造块");
}
static {
print("静态块");
}
public int l = print("l");
public static int o = print("o");
public Text(String str) {
System.out.println("构造:" + (++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println("print:" + (++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public int p = print("p");
public static void main(String args[]) {
Text t = new Text("init");
}
}
print:11:b i=0 n=0
print:12:a i=1 n=1
print:13:j i=2 n=2
print:14:m i=3 n=3
print:15:构造块 i=4 n=4
print:16:l i=5 n=5
print:17:p i=6 n=6
构造:18:t1 i=7 n=7
print:19:a i=8 n=8
print:20:j i=9 n=9
print:21:m i=10 n=10
print:22:构造块 i=11 n=11
print:23:l i=12 n=12
print:24:p i=13 n=13
构造:25:t2 i=14 n=14
print:26:i i=15 n=15
print:27:静态块 i=16 n=99
print:28:o i=17 n=100
print:29:a i=18 n=101
print:30:j i=19 n=102
print:31:m i=20 n=103
print:32:构造块 i=21 n=104
print:33:l i=22 n=105
print:34:p i=23 n=106
构造:35:init i=24 n=107
执行顺序:
1)JVM 类加载机制中提到,类连接 (验证, 准备, 解析)中准备工作:
负责为类的类变量(非对象变量)分配内存,并设置默认初始值,准备类中每个字段、方法和实现接口所需的数据结构, 这里说的初始值都是默认的值, 并不是程序中指定的值 , 经过准备工作,类中变量的初始值为如下
k =0; b=0; t1=null; t2=null; i=0; n=0;
2) JVM在类连接以后进行类的初始化,即给类变量赋值,按照静态属性的书写顺序执行
A: public static int k = 10; --> k=10
B:public static int b = print("b"); -->调用print("b") print:11:b i=0 n=0
C: public static Text t1 = new Text("t1"); 当加载静态变量是需要先加载构造器, 那就转为先加载所有非静态属性
此时按照书写的顺序加载非静态, 如下所示
public int a = print("a"); --》 print:12:a i=1 n=1
public int j = print("j"); --》print:13:j i=2 n=2
public int m = print("m"); --》print:14:m i=3 n=3
{
print("构造块"); --》print:15:构造块 i=4 n=4
}
public int l = print("l"); --》print:16:l i=5 n=5
public int p = print("p"); --》print:17:p i=6 n=6
然后继续执行构造器
public Text(String str) {
System.out.println("构造:" + (++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
==》构造:18:t1 i=7 n=7
D: public static Text t2 = new Text("t2"); 和C的过程一模一样, 非静态的每次new都加载一次
print:19:a i=8 n=8
print:20:j i=9 n=9
print:21:m i=10 n=10
print:22:构造块 i=11 n=11
print:23:l i=12 n=12
print:24:p i=13 n=13
构造:25:t2 i=14 n=14
E: public static int i = print("i"); ==>print:26:i i=15 n=15
F: public static int n = 99; n=99
G:
static {
print("静态块");
}
==>print:27:静态块 i=16 n=99
H: public static int o = print("o"); ==>print:28:o i=17 n=100
I: Text t = new Text("init");
==>
print:29:a i=18 n=101
print:30:j i=19 n=102
print:31:m i=20 n=103
print:32:构造块 i=21 n=104
print:33:l i=22 n=105
print:34:p i=23 n=106
构造:35:init i=24 n=107
参考: https://www..com/greatfish/p/5771548.html1·