这是面试或笔试中经常出现的一个问题:就是当子类继承父类之后,初始化子类,那么子类和父类各部分的加载顺序是怎样的?
答案是,顺序如下:
1. 父类的静态代码块 & 静态属性。(存在多个静态代码块 / 静态属性的话按照编写顺序由上至下依次执行)
2. 子类的静态代码块 & 静态属性。(存在多个静态代码块 / 静态属性的话按照编写顺序由上至下依次执行)
3. 父类的非静态代码块 & 非静态属性。(存在多个非静态代码块 / 非静态属性的话按照编写顺序由上至下依次执行)
4. 父类的构造函数。
5. 子类的非静态代码块 & 非静态属性。(存在多个非静态代码块 / 非静态属性的话按照编写顺序由上至下依次执行)
6. 子类的构造函数。
一共6项,看起来好像很多,但是其实都是遵循这么一条规则而已:
静态早于非静态的同时父类早于子类。
正是因为静态早于非静态。所以我们在写程序时,静态不能调用非静态,因为静态加载完成后,并不能保证非静态此时也加载完成了。但是非静态可以调用静态,因为你调用了非静态的时候非静态肯定先加载完成之后才能执行,而静态会早于非静态加载完成,此时静态也一定加载完成了。
而父类早于子类也很好理解,道理如同你不可能比你爹先出生,因为子类的很多行为和属性都要从父类身上获取下来才行。
那么我们通过代码来证实以上结论,为了验证,我们特地让静态及非静态的代码块和属性交叉排列:
父类,SuperClass:
public class SuperClass {
public String property1 = initProperty(1);
public static String staticProperty1 = initStaticProperty(1);
{
System.out.println("这是父类的非静态代码块1");
}
static {
System.out.println("这是父类的静态代码块1");
}
{
System.out.println("这是父类的非静态代码块2");
}
static {
System.out.println("这是父类的静态代码块2");
}
{
System.out.println("这是父类的非静态代码块3");
}
static {
System.out.println("这是父类的静态代码块3");
}
SuperClass() {
System.out.println("这是父类的构造函数");
}
public String property2 = initProperty(2);
public static String staticProperty2 = initStaticProperty(2);
public String property3 = initProperty(3);
public static String staticProperty3 = initStaticProperty(3);
public String initProperty(int order) {
System.out.println("这是父类的非静态属性" + order);
return "这是父类的非静态属性" + order;
}
public static String initStaticProperty(int order) {
System.out.println("这是父类的静态属性" + order);
return "这是父类的静态属性" + order;
}
}
子类,SubClass:
public class SubClass extends SuperClass{
{
System.out.println("这是子类的非静态代码块1");
}
static {
System.out.println("这是子类的静态代码块1");
}
{
System.out.println("这是子类的非静态代码块2");
}
static {
System.out.println("这是子类的静态代码块2");
}
{
System.out.println("这是子类的非静态代码块3");
}
static {
System.out.println("这是子类的静态代码块3");
}
public String property1 = initProperty(1);
public static String staticProperty1 = initStaticProperty(1);
public String property2 = initProperty(2);
public static String staticProperty2 = initStaticProperty(2);
public String property3 = initProperty(3);
public static String staticProperty3 = initStaticProperty(3);
public String initProperty(int order) {
System.out.println("这是子类的非静态属性" + order);
return "这是子类的非静态属性" + order;
}
public static String initStaticProperty(int order) {
System.out.println("这是子类的静态属性" + order);
return "这是子类的静态属性" + order;
}
SubClass() {
System.out.println("这是子类的构造函数");
}
}
随后我们初始化子类:
public static void main(String[] args) {
SubClass subClass = new SubClass();
}
可以看到控制台输出顺序如下:
通过观察下面几行打印以及代码可知:
(非)静态属性与(非)静态代码块的加载优先级一样高, 加载顺序取决于谁写在前面。
但是我们会通过下图发现一个问题:
哎,这里不是应该输出父类的非静态属性1,2,3吗,为什么是子类的?那是因为,父类和子类的非静态属性的初始化方法名都是 initProperty(),所以,子类是重写了父类的方法。这点一定要注意哦。
那么,接下来,我们修改子类的非静态属性的初始化方法名,以此来避免重写造成的多态现象,使我们更加直观的看到父类和子类的加载顺序:
子类代码修改后如下(就是将initProperty方法更名为subInitProperty):
package initorderstudy;
public class SubClass extends SuperClass{
{
System.out.println("这是子类的非静态代码块1");
}
static {
System.out.println("这是子类的静态代码块1");
}
{
System.out.println("这是子类的非静态代码块2");
}
static {
System.out.println("这是子类的静态代码块2");
}
{
System.out.println("这是子类的非静态代码块3");
}
static {
System.out.println("这是子类的静态代码块3");
}
public String property1 = subInitProperty(1);
public static String staticProperty1 = initStaticProperty(1);
public String property2 = subInitProperty(2);
public static String staticProperty2 = initStaticProperty(2);
public String property3 = subInitProperty(3);
public static String staticProperty3 = initStaticProperty(3);
// 将重写方法更名,以便不重写父类重名方法
public String subInitProperty(int order) {
System.out.println("这是子类的非静态属性" + order);
return "这是子类的非静态属性" + order;
}
public static String initStaticProperty(int order) {
System.out.println("这是子类的静态属性" + order);
return "这是子类的静态属性" + order;
}
SubClass() {
System.out.println("这是子类的构造函数");
}
}
执行main方法后结果如图:
由此,子类和父类个属性和代码块的加载顺序如上所述:静态早于非静态的同时父类早于子类。