java程序执行顺序(附:阿里面试题解析)
一般情况下程序的入口是main函数,但是一定是先加载main中的代码块吗?
答案:不一定 下面解释一下两种情况.
第一种
main方法在一个具有其他方法或属性的类中;
class Test{
public static int i=1;
public static void f(){
sout("静态方法");
}
public void print(){
sout("普通方法");
}
public static void main(String [] args){
new Test();
}
}
第二种:
public class Test{
public static void main(String [] args){
int i =0;
sout(i);
}
}
解析: 静态部分依赖于类,而不是依赖于对象存在.静态部分的加载优先于对象创建,当找到main方法后,JVM会加载main方法所在的类,试图找到类中其他静态的部分.
总结一下执行顺序:
- 静态属性,静态方法声明,静态块
- 动态属性,普通方法声明,构造块
- 构造方法
- 如果你不懂他们什么意思请参考Java中静态代码块、构造代码块、构造函数、普通代码块
下面是一道阿里的面试题
public class Text {
>1 public static int k = 0;
>2 public static Text t1 = new Text("t1");
>3public static Text t2 = new Text("t2");
>4 public static int i = print("i");
>5 public static int n = 99;
>6 public int j = print("j");
>7{
print("构造块");
}
>8 static {
print("静态块");
}
>9 public Text(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
>10 public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
>11 public static void main(String args[]) {
>12 sout("start");
>13 Text t = new Text("init");
}
}
运行结果:
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102
该题的注意点:
类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程,上面阿里那道面试题就是典型的暂停类加载
面试题debug过程
当程序开始时,先找到main入口即 11,这时加载该类里面的非动态属性.1-5顺序执行
当执行完1后,开始2代码,此时出现实例化过程但是被static约束,所以终断类加载,进行实例化过程(加载普通的属性,代码块,构造方法)
执行顺序6-10-6-7-10-9
2代码的实例化过程完成,分配了内存空间,接着执行3代码同样顺序
执行顺序6-10-6-7-10-9
3代码的实例化过程完成,分配了内存空间,开始类加载
4代码执行
执行顺序4-10
5代码执行
8代码执行
10代码执行
这个时候类加载完毕
12代码执行
13代码执行,进行实例化
执行顺序6-10-6-7-10-9-13
程序结束
总结(静态过程为类加载)
1.类中所有属性的默认值(一举而成)
2. 父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)
3. 子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)
4. 调用父类的构造方法,
首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后父类构造方法
5. 调用子类的构造方法,
首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后子类构造方法