java中构造代码块、方法调用顺序问题

1. 继承的概念

  继承在本职上是特殊——一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。

2. 继承中的初始化顺序

  从类的结构上而言,其内部可以有如下四种常见形态:属性(包括类属性和实例属性)、方法(包括类方法和实例方法)、构造器和初始化块(包括类的初始化块和实例的初始化块)。对于继承中的初始化顺序,又具体分为类的初始化和对象的初始化。

类初始化:

  在jvm装载类的准备阶段,首先为类的所有类属性和类初始化块分配内存空间。并在类首次初始化阶段中为其进行初始化,类属性和类初始化块之间的定义时的顺序决定了其初始化的顺序。若类存在父类,则首先初始化父类的类属性和类初始化块,一直上溯到Object类最先执行。

对象初始化:

  在new创建对象时,首先对对象属性和初始化块分配内存,并执行默认初始化。如果存在父类,则先为父类对象属和初始化块先分配内存并执行初始化。然后执行父类构造器中的初始化程序,接着才开始对子类的对象属性和初始化块执行初始化。

  调用顺序:父类的类属性和类代码块-->子类的类属性和类代码块-->父类的对象属性和代码块-->父类的构造函数-->子类的对象属性和代码块-->子类的构造函数

3、举例说明

//先静态初始化并且只有一次!!然后构造代码块!!再构造方法!!  
//声明一个继承自父类的子类时,先把父类的构造代码块及构造方法初始化然后初始化子类的构造代码块和构造方法  
  
class Parent {  
    public static String p_StaticField = "父类--静态变量";  
    public String p_Field = "父类--变量";  
    // 代码块  
      
  
    // 构造函数  
      
    public Parent() {  
        System.out.println("父类--构造器");  
    }  
    {  
        System.out.println(p_Field);  
        System.out.println("父类--初始化块");  
    }  
  
    // 静态初始化块  
    static {  
        System.out.println(p_StaticField);  
        System.out.println("父类--静态初始化块");  
    }  
}  
  
public class StaticBlock extends Parent {  
    public static String s_StaticField = "子类--静态变量";  
    public String s_Field = "子类--变量";  
    {  
        System.out.println(s_Field);  
        System.out.println("子类--初始化块");  
    }  
  
    // 静态初始化块  
    static {  
        System.out.println(s_StaticField);  
        System.out.println("子类--静态初始化块");  
    }  
  
    public StaticBlock() {  
        System.out.println("子类--构造器");  
    }  
  
    public static void main(String[] args) {  
        System.out.println("-------------------");  
        new Parent();  
        System.out.println("-------------------");  
        new StaticBlock();  
    }  
}  
/**Output
 * 父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
-------------------
父类--变量
父类--初始化块
父类--构造器
-------------------
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
 */

注:

1. 在对象初始化阶段,属性和方法均针对子类可以从父类继承过来的属性和方法而言,一般而言,都是针对父类中非private而言的。因为private修饰的为父类所特有的,子类没有继承过来,当new子类时,无须为其分配空间并执行初始化。当然了,父类的构造器子类也是不继承过来的,但构造器另当别论。

2. 类的初始化只执行一次,当对同一个类new多个对象时,类属性和类初始化块只初始化一次。

4、注意构造函数中的多态问题

  在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。

class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    }
    Glyph() {
        System.out.println("Glyph() before draw()");//1
        draw();
        System.out.println("Glyph() after draw()");//3
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;//4

    RoundGlyph(int r) {
        radius = r;//5
        System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius);//6
    }

    void draw() {
        System.out.println("RoundGlyph.draw(). radius = " + radius);//2
    }
}

public class PolyConstructors {

    public static void main(String[] args) {
        new RoundGlyph(5);

    }
}

/**
Glyph() before draw()
RoundGlyph.draw(). radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(). radius = 5
*/
原文地址:https://www.cnblogs.com/hdulzt/p/7722190.html