Java类加载的顺序-包含父子关系
首先看一段代码:
public class TestClassLoader {
static class Father {
public static final String TAG = "Father";
static {
System.out.println("static Father");
}
{
System.out.println("unStatic Father");
}
public Father() {
System.out.println("constract Father");
method();
}
public void method() {
System.out.println("method Father");
}
@Override
public String toString() {
return "toString Father";
}
}
static class Son extends Father {
public static Son instance = new Son();
static {
System.out.println("static Son");
}
{
System.out.println("unStatic Son");
}
public Son() {
System.out.println("constract Son");
method();
}
public void method() {
System.out.println("method Son");
}
@Override
public String toString() {
return "toString Son";
}
}
public static void main(String[] args) {
// System.out.println("1.---------------------");
// System.out.println(Son.TAG);
// Son[] sons = new Son[10];
// System.out.println(sons);
// System.out.println("2.---------------------");
System.out.println(Son.instance);
// System.out.println("3.---------------------");
// Son son = new Son();
// Father father = son;
// father.method();
// System.out.println(son);
}
}
大家思考一下,上面代码执行以后的结果是什么样子的?
static Father
unStatic Father
constract Father
method Son
unStatic Son
constract Son
method Son
static Son
toString Son
我们会看到执行结果如上图所示。
那具体类加载的一个顺序到底是什么样子的呢?
1.首先静态代码块和静态变量肯定是要先进行加载的,并且同类里面的加载顺序是按代码顺序执行的
2.在调用构造方法的时候,会在构造方法之前,加载非静态代码块
3.最后才是调用类的构造方法
4.如果是父子关系,上面的每一步操作都需要先加载父类的相应方法
注意点:
1.静态代码块只会加载一次;
2.构造父类时,其所调用的静态成员和非静态属性是父类的,但是非静态的方法是子类的;
3.子类直接调用父类的静态变量,是不会加载子类的;
4.如果直接请求类的static final 修饰的变量(也就是在常量池中),类是不会被加载和初始化的;
那下面我们来针对于上面的代码进行分析;
我们执行 Son.instance 那程序会加载Son类,
根据我们上面的第4条规则,由于Son类是有父类的,所以我们首先需要加载父类的静态代码块------打印 :static Father
然后执行子类的静态变量和静态代码块,根据第1条规则,同类静态加载按照代码顺序,此时我们会进行 new Son()
根据第1和第2条规则,我们首先会调用父类的非静态代码块-------打印:unStatic Father
然后执行父类构造方法,打印:constract Father
在父类构造方法中有调用非静态方法,根据注意点2,我们知道直接调用子类的方法,打印:method Son
父类构造完成之后,会按照1和2规则执行子类,会执行子类的非静态代码块,打印:unStatic Son
然后执行子类的构造,打印:constract Son
在子类构造中,调用自己的方法,打印:method Son
此时执行完了子类的第一步静态变量的加载,按照第1条规则,继续执行子类的静态方法,打印:static Son
到这里就完成了整个加载过程;
最后由于调用了 System.out.println 结果会执行子类的toString方法,打印:toString Son。