首先把结论贴出来:
1. 类的加载顺序。
(1) 父类静态代码块(包括静态初始化块,静态属性,但不包括静态方法)
(2) 子类静态代码块(包括静态初始化块,静态属性,但不包括静态方法 )
(3) 父类非静态代码块( 包括非静态初始化块,非静态属性 )
(4) 父类构造函数
(5) 子类非静态代码块 ( 包括非静态初始化块,非静态属性 )
(6) 子类构造函数
Point1:关于静态代码块(包括静态属性的声明和静态的代码块),是严格按照在类中的出现顺序执行的。如果你在前面声明了一个静态属性 static int mm = 1; 那么你可以在这句声明位置之后的静态代码块中引用该属性,如 static { mm += 1}; ,但是绝对不能在声明该静态属性之前引用该属性,否则编译不会通过,而是会报前向引用的错误。
Point2:对于类的非静态代码块(非静态代码块和非静态属性的声明),我们可以简单地将它们全部按照顺序写在该类的所有的构造器方法(不管有参还是无参)的开头,在执行顺序上与P1一样,也是严格按照出现顺序执行的。
Point3:根据以上两个结论,对于一个要分析加载顺序的类,可以把它改写成只有两个部分的类,即静态部分和非静态部分。静态部分是将所有的static属性以及static代码块全部按照顺序写在一起,非静态部分则是把非static的属性和非static的代码块全部按照顺序写在所有的构造器方法的最开头。 这样就可以把类的加载顺序合成为:
- 父类静态代码块部分
- 子类静态代码块部分
- 父类构造器
- 子类构造器
2. 我们知道,如果调用子类的无参构造器,则会在子类的构造器方法的开头默认调用父类的构造器方法super();那么,如果调用子类的含参构造器方法,是否会调用父类的有参构造器方法或是无参构造器?
答案是: 不管调用的子类构造器方法是有参还是无参的,只要没有在构造器方法的开头显式地写出super(a,b,c)这样调用有参父类构造