Java笔记 ·

Java代码块执行顺序初探

1、仅构造方法

每个类都有构造方法。

如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。

若定义了有参构造方法,编译器将不会提供默认的无参构造,如需要,需要自己构造。

Java继承中对构造函数是不继承的,只是显式或者隐式调用。

实例

父类A

子类B

测试类E

测试结果

解析

Java继承中对构造函数是不继承的,只是显式或者隐式调用,并且必须是在构造函数第一行。这里是隐式调用了super()。

子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。

如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。

所以先执行父类构造函数,再执行子类构造函数。

2、静态块、构造方法

静态代码块:在java中使用static关键字声明的代码块。

实例1:

父类A

子类B

测试类E

测试结果

解析

静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。

由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。

如果类中包含多个静态代码块,那么将按照"先定义的代码先执行,后定义的代码后执行"。

静态代码块不能存在于任何方法体内。

静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。

当父类与子类都有静态代码块和构造函数的时候,执行顺序如下:

  • 父类静态代码块 > 子类静态代码块(Java虚拟机加载类时,就会执行该块代码)。
  • 父类构造函数 > 子类构造函数 (先有父亲,后有孩子)
  • 如果是多级继承关系的话,高层的父类首先执行,然后依次递减。

总结:静态优先执行,父类优先于子类执行。 静态代码块是在JVM加载类的时候执行的,而且静态代码块执行且仅执行一次

实例2:

子类包含静态属性---子类的实例化。

父类A:同实例1
子类B:

测试类F

测试结果

解析
进入main函数,先执行

得到

执行

后,先执行父类的静态块得到

执行子类静态成员变量(方法块),由于子类包含静态属性和方法,按照“先定义的代码先执行,后定义的代码后执行”的规则,先执行静态属性部分,即:

此时由于父类静态部分已经执行,故执行相应的构造函数得到(感觉说是中断类加载过程,执行实例初始化过程更贴切些,这部分可以考虑参考看最后关于“暂停类加载”部分):

由于之后子类含有静态块,故继续执行静态块中的打印:

最后执行最终的无参构造函数:

若父类A,改成如下会更直观:

此时结果:

实例3:

子类和父类存在相同的静态方法和非静态方法时
父类A

子类B

测试类F

测试结果

解析
java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏。

原因:

1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。
3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。

3、构造代码块、静态块、构造方法

构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。

构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。

实例1

父类A

子类B

测试类E

测试结果

解析
先静态后构造块,先父类后子类。

实例2:

对于非继承类J

测试结果

解析
典型的暂停类加载。即:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程。

类加载过程,不涉及构造方法。
实例化过程,涉及构造方法。

先类加载,执行static修饰的部分,遇到属性实例化,中断去执行实例化,完成后继续类加载,如此循环直至结束。

小结

无继承的的初始化顺序

静态成员变量(静态代码块)→普通成员变量→构造器

有继承的初始化顺序

父类静态成员变量、静态代码块→子类静态成员变量、静态代码块→父类普通成员变量、普通代码块→父类构造器→子类普通成员变量、普通代码块→子类构造器。

其他

类加载过程,不涉及构造方法

实例化过程,涉及构造方法

1、类中所有属性的默认值(一举而成)

2、父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)

3、子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)

4 、调用父类的构造方法,

首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)

然后父类构造方法

5、 调用子类的构造方法,

首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)

然后子类构造方法

参考资料

深入了解Java程序执行顺序

Java中普通代码块,构造代码块,静态代码块区别及代码示例

Java:构造器,构造代码块,静态代码块的执行顺序

Java 继承

在继承中的问题,关于静态代码块,子类和父类的静态代码块的执行情况

JAVA静态方法是否可以被继承?

参与评论